Merge "Create BluetoothDetailsViewModel and embed bluetooth xml dialog in it." into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index 45e33ce..2f843f9 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -345,7 +345,7 @@
     name: "android.nfc.flags-aconfig",
     package: "android.nfc",
     container: "system",
-    srcs: ["nfc/java/android/nfc/*.aconfig"],
+    srcs: ["nfc-non-updatable/flags/*.aconfig"],
 }
 
 cc_aconfig_library {
@@ -401,6 +401,7 @@
     min_sdk_version: "30",
     apex_available: [
         "//apex_available:platform",
+        "com.android.tethering",
         "com.android.wifi",
     ],
     defaults: ["framework-minus-apex-aconfig-java-defaults"],
@@ -1215,6 +1216,7 @@
 // DevicePolicy
 aconfig_declarations {
     name: "device_policy_aconfig_flags",
+    exportable: true,
     package: "android.app.admin.flags",
     container: "system",
     srcs: [
@@ -1233,6 +1235,7 @@
     aconfig_declarations: "device_policy_aconfig_flags",
     defaults: ["framework-minus-apex-aconfig-java-defaults"],
     min_sdk_version: "30",
+    mode: "exported",
     apex_available: [
         "//apex_available:platform",
         "com.android.permission",
diff --git a/Android.bp b/Android.bp
index 529da53..a1f6e30 100644
--- a/Android.bp
+++ b/Android.bp
@@ -530,6 +530,50 @@
         ],
     },
     jarjar_prefix: "com.android.internal.hidden_from_bootclasspath",
+
+    jarjar_shards: select(release_flag("RELEASE_USE_SHARDED_JARJAR_ON_FRAMEWORK_MINUS_APEX"), {
+        true: "10",
+        default: "1",
+    }),
+}
+
+// This is identical to "framework-minus-apex" but with "jarjar_shards" hardcodd.
+// (also "stem" is commented out to avoid a conflict with the "framework-minus-apex")
+// TODO(b/383559945) This module is just for local testing / verification. It's not used
+// by anything. Remove it once we roll out RELEASE_USE_SHARDED_JARJAR_ON_FRAMEWORK_MINUS_APEX.
+java_library {
+    name: "framework-minus-apex_jarjar-sharded",
+    defaults: [
+        "framework-minus-apex-with-libs-defaults",
+        "framework-non-updatable-lint-defaults",
+    ],
+    installable: true,
+    // For backwards compatibility.
+    // stem: "framework",
+    apex_available: ["//apex_available:platform"],
+    visibility: [
+        "//frameworks/base",
+        "//frameworks/base/location",
+        // TODO(b/147128803) remove the below lines
+        "//frameworks/base/apex/blobstore/framework",
+        "//frameworks/base/apex/jobscheduler/framework",
+        "//frameworks/base/packages/Tethering/tests/unit",
+        "//packages/modules/Connectivity/Tethering/tests/unit",
+    ],
+    errorprone: {
+        javacflags: [
+            "-Xep:AndroidFrameworkCompatChange:ERROR",
+            "-Xep:AndroidFrameworkUid:ERROR",
+        ],
+    },
+    lint: {
+        baseline_filename: "lint-baseline.xml",
+        warning_checks: [
+            "FlaggedApi",
+        ],
+    },
+    jarjar_prefix: "com.android.internal.hidden_from_bootclasspath",
+    jarjar_shards: "10",
 }
 
 java_library {
diff --git a/OWNERS b/OWNERS
index d0a634e..058ea36 100644
--- a/OWNERS
+++ b/OWNERS
@@ -34,6 +34,8 @@
 per-file *ravenwood* = file:ravenwood/OWNERS
 per-file *Ravenwood* = file:ravenwood/OWNERS
 
+per-file PREUPLOAD.cfg = file:/PREUPLOAD_OWNERS
+
 per-file INPUT_OWNERS = file:/INPUT_OWNERS
 per-file ZYGOTE_OWNERS = file:/ZYGOTE_OWNERS
 per-file SQLITE_OWNERS = file:/SQLITE_OWNERS
@@ -48,3 +50,4 @@
 per-file ADPF_OWNERS = file:/ADPF_OWNERS
 per-file GAME_MANAGER_OWNERS = file:/GAME_MANAGER_OWNERS
 per-file SDK_OWNERS = file:/SDK_OWNERS
+per-file PREUPLOAD_OWNERS = file:/PREUPLOAD_OWNERS
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index d83109a..5e0428b 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -18,7 +18,7 @@
                tests/
                tools/
 bpfmt = -d
-ktfmt = --kotlinlang-style --include-dirs=services/permission,packages/SystemUI,libs/WindowManager/Shell/src/com/android/wm/shell/freeform,libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode
+ktfmt = --kotlinlang-style --include-dirs=services/permission,packages/SystemUI,libs/WindowManager/Shell/src/com/android/wm/shell/freeform,libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode,libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode
 
 [Hook Scripts]
 checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py --sha ${PREUPLOAD_COMMIT}
diff --git a/PREUPLOAD_OWNERS b/PREUPLOAD_OWNERS
new file mode 100644
index 0000000..ece4d3e
--- /dev/null
+++ b/PREUPLOAD_OWNERS
@@ -0,0 +1,2 @@
+roosa@google.com
+gsennton@google.com
diff --git a/apct-tests/perftests/core/src/android/content/pm/SystemFeaturesMetadataPerfTest.java b/apct-tests/perftests/core/src/android/content/pm/SystemFeaturesMetadataPerfTest.java
new file mode 100644
index 0000000..205c7b8
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/content/pm/SystemFeaturesMetadataPerfTest.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class SystemFeaturesMetadataPerfTest {
+    // As each query is relatively cheap, add an inner iteration loop to reduce execution noise.
+    private static final int NUM_ITERATIONS = 10;
+
+    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+    @Test
+    public void maybeGetSdkFeatureIndex_featureDefined() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            for (int i = 0; i < NUM_ITERATIONS; ++i) {
+                PackageManager.maybeGetSdkFeatureIndex(PackageManager.FEATURE_WATCH);
+                PackageManager.maybeGetSdkFeatureIndex(PackageManager.FEATURE_LEANBACK);
+                PackageManager.maybeGetSdkFeatureIndex(PackageManager.FEATURE_IPSEC_TUNNELS);
+                PackageManager.maybeGetSdkFeatureIndex(PackageManager.FEATURE_WEBVIEW);
+                PackageManager.maybeGetSdkFeatureIndex(PackageManager.FEATURE_NFC_BEAM);
+                PackageManager.maybeGetSdkFeatureIndex(PackageManager.FEATURE_AUTOFILL);
+            }
+        }
+    }
+
+    @Test
+    public void maybeGetSdkFeatureIndex_featureUndefined() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            for (int i = 0; i < NUM_ITERATIONS; ++i) {
+                PackageManager.maybeGetSdkFeatureIndex("com.android.custom.feature.1");
+                PackageManager.maybeGetSdkFeatureIndex("com.android.custom.feature.2");
+                PackageManager.maybeGetSdkFeatureIndex("foo");
+                PackageManager.maybeGetSdkFeatureIndex("bar");
+                PackageManager.maybeGetSdkFeatureIndex("0");
+                PackageManager.maybeGetSdkFeatureIndex("");
+            }
+        }
+    }
+
+}
diff --git a/apct-tests/perftests/core/src/android/libcore/ZipFilePerfTest.java b/apct-tests/perftests/core/src/android/libcore/ZipFilePerfTest.java
index ed669be..c775280 100644
--- a/apct-tests/perftests/core/src/android/libcore/ZipFilePerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/ZipFilePerfTest.java
@@ -63,14 +63,12 @@
 
     @Test
     @Parameters(method = "getData")
-    public void timeZipFileOpen(int numEntries) throws Exception {
+    public void timeZipFileOpenClose(int numEntries) throws Exception {
         setUp(numEntries);
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
             ZipFile zf = new ZipFile(mFile);
-            state.pauseTiming();
             zf.close();
-            state.resumeTiming();
         }
     }
 
diff --git a/apct-tests/perftests/utils/src/android/perftests/utils/BenchmarkState.java b/apct-tests/perftests/utils/src/android/perftests/utils/BenchmarkState.java
index 73bff08..af02374 100644
--- a/apct-tests/perftests/utils/src/android/perftests/utils/BenchmarkState.java
+++ b/apct-tests/perftests/utils/src/android/perftests/utils/BenchmarkState.java
@@ -20,6 +20,7 @@
 import android.app.Instrumentation;
 import android.os.Bundle;
 import android.os.Debug;
+import android.os.Trace;
 import android.util.Log;
 
 import androidx.test.InstrumentationRegistry;
@@ -129,17 +130,23 @@
     }
 
     private void beginWarmup() {
+        Trace.beginSection("Warmup");
         mStartTimeNs = System.nanoTime();
         mIteration = 0;
         mState = WARMUP;
     }
 
+    private void endWarmup() {
+        Trace.endSection();
+    }
+
     private void beginBenchmark(long warmupDuration, int iterations) {
         if (ENABLE_PROFILING) {
             File f = new File(InstrumentationRegistry.getContext().getDataDir(), "benchprof");
             Log.d(TAG, "Tracing to: " + f.getAbsolutePath());
             Debug.startMethodTracingSampling(f.getAbsolutePath(), 16 * 1024 * 1024, 100);
         }
+        Trace.beginSection("Benchmark");
         mMaxIterations = (int) (TARGET_TEST_DURATION_NS / (warmupDuration / iterations));
         mMaxIterations = Math.min(MAX_TEST_ITERATIONS,
                 Math.max(mMaxIterations, MIN_TEST_ITERATIONS));
@@ -150,6 +157,10 @@
         mStartTimeNs = System.nanoTime();
     }
 
+    private void endBenchmark() {
+        Trace.endSection();
+    }
+
     private boolean startNextTestRun() {
         final long currentTime = System.nanoTime();
         mResults.add((currentTime - mStartTimeNs - mPausedDurationNs) / mMaxIterations);
@@ -165,6 +176,7 @@
                 return true;
             }
             mState = FINISHED;
+            endBenchmark();
             return false;
         }
         mPausedDurationNs = 0;
@@ -189,6 +201,7 @@
                 // don't yet have a target iteration count.
                 final long duration = System.nanoTime() - mStartTimeNs;
                 if (mIteration >= WARMUP_MIN_ITERATIONS && duration >= WARMUP_DURATION_NS) {
+                    endWarmup();
                     beginBenchmark(duration, mIteration);
                 }
                 return true;
@@ -208,6 +221,7 @@
                 mCustomizedIterations++;
                 if (mCustomizedIterations >= mMaxCustomizedIterations) {
                     mState = FINISHED;
+                    endBenchmark();
                     return false;
                 }
                 mCustomizedIterationListener.onStart(mCustomizedIterations);
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobParameters.java b/apex/jobscheduler/framework/java/android/app/job/JobParameters.java
index b6f0c04..7fef4e5 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobParameters.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobParameters.java
@@ -23,6 +23,10 @@
 import android.annotation.TestApi;
 import android.app.ActivityManager;
 import android.app.usage.UsageStatsManager;
+import android.compat.Compatibility;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.Disabled;
+import android.compat.annotation.Overridable;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ClipData;
 import android.content.pm.PackageManager;
@@ -349,6 +353,16 @@
     private JobCleanupCallback mJobCleanupCallback;
     @Nullable
     private Cleaner.Cleanable mCleanable;
+    /**
+     * Override handling of abandoned jobs in the system. Overriding this change
+     * will prevent the system to handle abandoned jobs and report it as a new
+     * stop reason STOP_REASON_TIMEOUT_ABANDONED.
+     * @hide
+     */
+    @ChangeId
+    @Disabled
+    @Overridable
+    public static final long OVERRIDE_HANDLE_ABANDONED_JOBS = 372529068L;
 
     /** @hide */
     public JobParameters(IBinder callback, String namespace, int jobId, PersistableBundle extras,
@@ -677,6 +691,10 @@
      * @hide
      */
     public void enableCleaner() {
+        if (!Flags.handleAbandonedJobs()
+                || Compatibility.isChangeEnabled(OVERRIDE_HANDLE_ABANDONED_JOBS)) {
+            return;
+        }
         // JobParameters objects are passed by reference in local Binder
         // transactions for clients running as SYSTEM. The life cycle of the
         // JobParameters objects are no longer controlled by the client.
@@ -695,6 +713,10 @@
      * @hide
      */
     public void disableCleaner() {
+        if (!Flags.handleAbandonedJobs()
+                || Compatibility.isChangeEnabled(OVERRIDE_HANDLE_ABANDONED_JOBS)) {
+            return;
+        }
         if (mJobCleanupCallback != null) {
             mJobCleanupCallback.disableCleaner();
             if (mCleanable != null) {
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobServiceEngine.java b/apex/jobscheduler/framework/java/android/app/job/JobServiceEngine.java
index 69b83cc..d460dcc 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobServiceEngine.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobServiceEngine.java
@@ -165,11 +165,9 @@
                 case MSG_EXECUTE_JOB: {
                     final JobParameters params = (JobParameters) msg.obj;
                     try {
-                        if (Flags.handleAbandonedJobs()) {
-                            params.enableCleaner();
-                        }
+                        params.enableCleaner();
                         boolean workOngoing = JobServiceEngine.this.onStartJob(params);
-                        if (Flags.handleAbandonedJobs() && !workOngoing) {
+                        if (!workOngoing) {
                             params.disableCleaner();
                         }
                         ackStartMessage(params, workOngoing);
@@ -196,9 +194,7 @@
                     IJobCallback callback = params.getCallback();
                     if (callback != null) {
                         try {
-                            if (Flags.handleAbandonedJobs()) {
-                                params.disableCleaner();
-                            }
+                            params.disableCleaner();
                             callback.jobFinished(params.getJobId(), needsReschedule);
                         } catch (RemoteException e) {
                             Log.e(TAG, "Error reporting job finish to system: binder has gone" +
diff --git a/apex/jobscheduler/service/aconfig/job.aconfig b/apex/jobscheduler/service/aconfig/job.aconfig
index fe95a59..86ed06b 100644
--- a/apex/jobscheduler/service/aconfig/job.aconfig
+++ b/apex/jobscheduler/service/aconfig/job.aconfig
@@ -95,4 +95,14 @@
    namespace: "backstage_power"
    description: "Apply the quota policy to jobs started when the app was in TOP state"
    bug: "374323858"
+}
+
+flag {
+    name: "enforce_schedule_limit_to_proxy_jobs"
+    namespace: "backstage_power"
+    description: "Limit the schedule calls towards the persisted proxy jobs"
+    bug: "377912323"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
 }
\ No newline at end of file
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
index 8f44698..5dfb375 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
@@ -1034,7 +1034,7 @@
                 for (int p = preferredUidOnly.size() - 1; p >= 0; --p) {
                     final ContextAssignment assignment = preferredUidOnly.get(p);
                     final JobStatus runningJob = assignment.context.getRunningJobLocked();
-                    if (runningJob.getUid() != nextPending.getUid()) {
+                    if (runningJob == null || runningJob.getUid() != nextPending.getUid()) {
                         continue;
                     }
                     final int jobBias = mService.evaluateJobBiasLocked(runningJob);
@@ -1916,8 +1916,9 @@
         for (int i = 0; i < mActiveServices.size(); i++) {
             final JobServiceContext jc = mActiveServices.get(i);
             final JobStatus js = jc.getRunningJobLocked();
-            if (jc.stopIfExecutingLocked(pkgName, userId, namespace, matchJobId, jobId,
-                    stopReason, internalStopReason)) {
+            if (js != null &&
+                    jc.stopIfExecutingLocked(pkgName, userId, namespace,
+                        matchJobId, jobId, stopReason, internalStopReason)) {
                 foundSome = true;
                 pw.print("Stopping job: ");
                 js.printUniqueId(pw);
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index 8fad79a..4335cae 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -16,6 +16,7 @@
 
 package com.android.server.job;
 
+import static android.app.job.JobParameters.OVERRIDE_HANDLE_ABANDONED_JOBS;
 import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
 import static android.Manifest.permission.MANAGE_ACTIVITY_TASKS;
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
@@ -1718,8 +1719,9 @@
             int userId, @Nullable String namespace, String tag) {
         // Rate limit excessive schedule() calls.
         final String servicePkg = job.getService().getPackageName();
-        if (job.isPersisted() && (packageName == null || packageName.equals(servicePkg))) {
-            // Only limit schedule calls for persisted jobs scheduled by the app itself.
+        if (job.isPersisted() && (Flags.enforceScheduleLimitToProxyJobs()
+                || (packageName == null || packageName.equals(servicePkg)))) {
+            // limit excessive schedule calls for persisted jobs.
             final String pkg = packageName == null ? servicePkg : packageName;
             if (!mQuotaTracker.isWithinQuota(userId, pkg, QUOTA_TRACKER_SCHEDULE_PERSISTED_TAG)) {
                 if (mQuotaTracker.isWithinQuota(userId, pkg, QUOTA_TRACKER_SCHEDULE_LOGGED)) {
@@ -1985,8 +1987,8 @@
                     jobStatus.getNumAbandonedFailures(),
                     /* 0 is reserved for UNKNOWN_POLICY */
                     jobStatus.getJob().getBackoffPolicy() + 1,
-                    shouldUseAggressiveBackoff(jobStatus.getNumAbandonedFailures()));
-
+                    shouldUseAggressiveBackoff(
+                            jobStatus.getNumAbandonedFailures(), jobStatus.getSourceUid()));
 
             // If the job is immediately ready to run, then we can just immediately
             // put it in the pending list and try to schedule it.  This is especially
@@ -2431,7 +2433,8 @@
                     cancelled.getNumAbandonedFailures(),
                     /* 0 is reserved for UNKNOWN_POLICY */
                     cancelled.getJob().getBackoffPolicy() + 1,
-                    shouldUseAggressiveBackoff(cancelled.getNumAbandonedFailures()));
+                    shouldUseAggressiveBackoff(
+                            cancelled.getNumAbandonedFailures(), cancelled.getSourceUid()));
         }
         // If this is a replacement, bring in the new version of the job
         if (incomingJob != null) {
@@ -3023,6 +3026,7 @@
         int numFailures = failureToReschedule.getNumFailures();
         int numAbandonedFailures = failureToReschedule.getNumAbandonedFailures();
         int numSystemStops = failureToReschedule.getNumSystemStops();
+        final int uid = failureToReschedule.getSourceUid();
         // We should back off slowly if JobScheduler keeps stopping the job,
         // but back off immediately if the issue appeared to be the app's fault
         // or the user stopped the job somehow.
@@ -3032,6 +3036,7 @@
                 || stopReason == JobParameters.STOP_REASON_USER) {
             numFailures++;
         } else if (android.app.job.Flags.handleAbandonedJobs()
+                && !CompatChanges.isChangeEnabled(OVERRIDE_HANDLE_ABANDONED_JOBS, uid)
                 && internalStopReason == JobParameters.INTERNAL_STOP_REASON_TIMEOUT_ABANDONED) {
             numAbandonedFailures++;
             numFailures++;
@@ -3040,7 +3045,7 @@
         }
 
         int backoffPolicy = job.getBackoffPolicy();
-        if (shouldUseAggressiveBackoff(numAbandonedFailures)) {
+        if (shouldUseAggressiveBackoff(numAbandonedFailures, uid)) {
             backoffPolicy = JobInfo.BACKOFF_POLICY_EXPONENTIAL;
         }
 
@@ -3111,8 +3116,9 @@
      * @return {@code true} if the given number of abandoned failures indicates that JobScheduler
      *     should use an aggressive backoff policy.
      */
-    public boolean shouldUseAggressiveBackoff(int numAbandonedFailures) {
+    public boolean shouldUseAggressiveBackoff(int numAbandonedFailures, int uid) {
         return android.app.job.Flags.handleAbandonedJobs()
+                && !CompatChanges.isChangeEnabled(OVERRIDE_HANDLE_ABANDONED_JOBS, uid)
                 && numAbandonedFailures
                         > mConstants.ABANDONED_JOB_TIMEOUTS_BEFORE_AGGRESSIVE_BACKOFF;
     }
@@ -3222,7 +3228,9 @@
     @VisibleForTesting
     void maybeProcessBuggyJob(@NonNull JobStatus jobStatus, int debugStopReason) {
         boolean jobTimedOut = debugStopReason == JobParameters.INTERNAL_STOP_REASON_TIMEOUT;
-        if (android.app.job.Flags.handleAbandonedJobs()) {
+        if (android.app.job.Flags.handleAbandonedJobs()
+                && !CompatChanges.isChangeEnabled(
+                        OVERRIDE_HANDLE_ABANDONED_JOBS, jobStatus.getSourceUid())) {
             jobTimedOut |= (debugStopReason
                 == JobParameters.INTERNAL_STOP_REASON_TIMEOUT_ABANDONED);
         }
@@ -3308,6 +3316,8 @@
         final JobStatus rescheduledJob = needsReschedule
                 ? getRescheduleJobForFailureLocked(jobStatus, stopReason, debugStopReason) : null;
         final boolean isStopReasonAbandoned = android.app.job.Flags.handleAbandonedJobs()
+                && !CompatChanges.isChangeEnabled(
+                        OVERRIDE_HANDLE_ABANDONED_JOBS, jobStatus.getSourceUid())
                 && (debugStopReason == JobParameters.INTERNAL_STOP_REASON_TIMEOUT_ABANDONED);
         if (rescheduledJob != null
                 && !rescheduledJob.shouldTreatAsUserInitiatedJob()
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 2b401c8..ebfda52 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
@@ -16,6 +16,8 @@
 
 package com.android.server.job;
 
+import static android.app.job.JobParameters.OVERRIDE_HANDLE_ABANDONED_JOBS;
+
 import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_NONE;
 import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
 import static com.android.server.job.JobSchedulerService.safelyScaleBytesToKBForHistogram;
@@ -550,7 +552,8 @@
                     job.getNumAbandonedFailures(),
                     /* 0 is reserved for UNKNOWN_POLICY */
                     job.getJob().getBackoffPolicy() + 1,
-                    mService.shouldUseAggressiveBackoff(job.getNumAbandonedFailures()));
+                    mService.shouldUseAggressiveBackoff(
+                            job.getNumAbandonedFailures(), job.getSourceUid()));
             sEnqueuedJwiAtJobStart.logSampleWithUid(job.getUid(), job.getWorkCount());
             final String sourcePackage = job.getSourcePackageName();
             if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
@@ -1461,7 +1464,10 @@
                     final StringBuilder debugStopReason = new StringBuilder("client timed out");
 
                     if (android.app.job.Flags.handleAbandonedJobs()
-                            && executing != null && executing.isAbandoned()) {
+                            && executing != null
+                            && !CompatChanges.isChangeEnabled(
+                                    OVERRIDE_HANDLE_ABANDONED_JOBS, executing.getSourceUid())
+                            && executing.isAbandoned()) {
                         final String abandonedMessage = " and maybe abandoned";
                         stopReason = JobParameters.STOP_REASON_TIMEOUT_ABANDONED;
                         internalStopReason = JobParameters.INTERNAL_STOP_REASON_TIMEOUT_ABANDONED;
@@ -1689,7 +1695,8 @@
                 completedJob.getNumAbandonedFailures(),
                 /* 0 is reserved for UNKNOWN_POLICY */
                 completedJob.getJob().getBackoffPolicy() + 1,
-                mService.shouldUseAggressiveBackoff(completedJob.getNumAbandonedFailures()));
+                mService.shouldUseAggressiveBackoff(
+                        completedJob.getNumAbandonedFailures(), completedJob.getSourceUid()));
         if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
             Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_SYSTEM_SERVER,
                     JobSchedulerService.TRACE_TRACK_NAME, getId());
diff --git a/api/ApiDocs.bp b/api/ApiDocs.bp
index 89351fd..03fb44f 100644
--- a/api/ApiDocs.bp
+++ b/api/ApiDocs.bp
@@ -130,6 +130,10 @@
 droidstubs {
     name: "framework-doc-stubs",
     defaults: ["android-non-updatable-doc-stubs-defaults"],
+    flags: [
+        // Ignore any compatibility errors, see check_api.last_released below for more information.
+        "--hide-category Compatibility",
+    ],
     srcs: [":all-modules-public-stubs-source-exportable"],
     api_levels_module: "api_versions_public",
     aidl: {
@@ -138,13 +142,39 @@
             "packages/modules/Media/apex/aidl/stable",
         ],
     },
+
+    // Pass the previously released API to support reverting flagged APIs. Without this, reverting
+    // a flagged API will cause it to be removed, even if it had previously been released. This
+    // has the side effect of causing compatibility issues to be reported but they are already
+    // checked elsewhere so they will be ignored, see `--hide-category Compatibility` above.
+    check_api: {
+        last_released: {
+            api_file: ":android.api.combined.public.latest",
+            removed_api_file: ":android-removed.api.combined.public.latest",
+        },
+    },
 }
 
 droidstubs {
     name: "framework-doc-system-stubs",
     defaults: ["framework-doc-stubs-sources-default"],
-    flags: ["--show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.PRIVILEGED_APPS\\)"],
+    flags: [
+        "--show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.PRIVILEGED_APPS\\)",
+        // Ignore any compatibility errors, see check_api.last_released below for more information.
+        "--hide-category Compatibility",
+    ],
     api_levels_module: "api_versions_system",
+
+    // Pass the previously released API to support reverting flagged APIs. Without this, reverting
+    // a flagged API will cause it to be removed, even if it had previously been released. This
+    // has the side effect of causing compatibility issues to be reported but they are already
+    // checked elsewhere so they will be ignored, see `--hide-category Compatibility` above.
+    check_api: {
+        last_released: {
+            api_file: ":android.api.combined.system.latest",
+            removed_api_file: ":android-removed.api.combined.system.latest",
+        },
+    },
 }
 
 /////////////////////////////////////////////////////////////////////
diff --git a/boot/preloaded-classes b/boot/preloaded-classes
index afd9984..b83bd4e 100644
--- a/boot/preloaded-classes
+++ b/boot/preloaded-classes
@@ -6583,6 +6583,7 @@
 android.permission.PermissionCheckerManager
 android.permission.PermissionControllerManager$1
 android.permission.PermissionControllerManager
+android.permission.PermissionManager
 android.permission.PermissionManager$1
 android.permission.PermissionManager$2
 android.permission.PermissionManager$OnPermissionsChangeListenerDelegate
diff --git a/cmds/screencap/screencap.cpp b/cmds/screencap/screencap.cpp
index 12de82a..d563ad3 100644
--- a/cmds/screencap/screencap.cpp
+++ b/cmds/screencap/screencap.cpp
@@ -416,7 +416,6 @@
         format = ANDROID_BITMAP_COMPRESS_FORMAT_PNG;
     } else if (jpeg) {
         format = ANDROID_BITMAP_COMPRESS_FORMAT_JPEG;
-        captureArgs.attachGainmap = true;
     }
 
     // setThreadPoolMaxThreadCount(0) actually tells the kernel it's
diff --git a/config/preloaded-classes b/config/preloaded-classes
index 343de0b..e53c78f 100644
--- a/config/preloaded-classes
+++ b/config/preloaded-classes
@@ -6587,6 +6587,7 @@
 android.permission.PermissionCheckerManager
 android.permission.PermissionControllerManager$1
 android.permission.PermissionControllerManager
+android.permission.PermissionManager
 android.permission.PermissionManager$1
 android.permission.PermissionManager$2
 android.permission.PermissionManager$OnPermissionsChangeListenerDelegate
diff --git a/config/preloaded-classes-denylist b/config/preloaded-classes-denylist
index 16f0693..e3e929c 100644
--- a/config/preloaded-classes-denylist
+++ b/config/preloaded-classes-denylist
@@ -3,7 +3,6 @@
 android.net.ConnectivityThread$Singleton
 android.os.FileObserver
 android.os.NullVibrator
-android.permission.PermissionManager
 android.provider.MediaStore
 android.speech.tts.TextToSpeech$Connection$SetupConnectionAsyncTask
 android.view.HdrRenderState
diff --git a/core/api/current.txt b/core/api/current.txt
index a9febc3..32507df 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -98,6 +98,7 @@
     field public static final String DUMP = "android.permission.DUMP";
     field public static final String ENFORCE_UPDATE_OWNERSHIP = "android.permission.ENFORCE_UPDATE_OWNERSHIP";
     field public static final String EXECUTE_APP_ACTION = "android.permission.EXECUTE_APP_ACTION";
+    field @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") public static final String EXECUTE_APP_FUNCTIONS = "android.permission.EXECUTE_APP_FUNCTIONS";
     field public static final String EXPAND_STATUS_BAR = "android.permission.EXPAND_STATUS_BAR";
     field public static final String FACTORY_TEST = "android.permission.FACTORY_TEST";
     field public static final String FOREGROUND_SERVICE = "android.permission.FOREGROUND_SERVICE";
@@ -280,6 +281,7 @@
     field public static final String REQUEST_COMPANION_PROFILE_COMPUTER = "android.permission.REQUEST_COMPANION_PROFILE_COMPUTER";
     field public static final String REQUEST_COMPANION_PROFILE_GLASSES = "android.permission.REQUEST_COMPANION_PROFILE_GLASSES";
     field public static final String REQUEST_COMPANION_PROFILE_NEARBY_DEVICE_STREAMING = "android.permission.REQUEST_COMPANION_PROFILE_NEARBY_DEVICE_STREAMING";
+    field @FlaggedApi("android.companion.virtualdevice.flags.enable_limited_vdm_role") public static final String REQUEST_COMPANION_PROFILE_SENSOR_DEVICE_STREAMING = "android.permission.REQUEST_COMPANION_PROFILE_SENSOR_DEVICE_STREAMING";
     field public static final String REQUEST_COMPANION_PROFILE_WATCH = "android.permission.REQUEST_COMPANION_PROFILE_WATCH";
     field public static final String REQUEST_COMPANION_RUN_IN_BACKGROUND = "android.permission.REQUEST_COMPANION_RUN_IN_BACKGROUND";
     field public static final String REQUEST_COMPANION_SELF_MANAGED = "android.permission.REQUEST_COMPANION_SELF_MANAGED";
@@ -1505,7 +1507,6 @@
     field public static final int shadowRadius = 16843108; // 0x1010164
     field public static final int shape = 16843162; // 0x101019a
     field public static final int shareInterpolator = 16843195; // 0x10101bb
-    field @FlaggedApi("android.nfc.nfc_associated_role_services") public static final int shareRolePriority;
     field @Deprecated public static final int sharedUserId = 16842763; // 0x101000b
     field @Deprecated public static final int sharedUserLabel = 16843361; // 0x1010261
     field public static final int sharedUserMaxSdkVersion = 16844365; // 0x101064d
@@ -1870,6 +1871,7 @@
     field public static final int wallpaperIntraOpenExitAnimation = 16843416; // 0x1010298
     field public static final int wallpaperOpenEnterAnimation = 16843411; // 0x1010293
     field public static final int wallpaperOpenExitAnimation = 16843412; // 0x1010294
+    field @FlaggedApi("android.nfc.nfc_associated_role_services") public static final int wantsRoleHolderPriority;
     field public static final int webTextViewStyle = 16843449; // 0x10102b9
     field public static final int webViewStyle = 16842885; // 0x1010085
     field public static final int weekDayTextAppearance = 16843592; // 0x1010348
@@ -8108,7 +8110,7 @@
     method @RequiresPermission(value=android.Manifest.permission.MANAGE_DEVICE_POLICY_PROFILE_INTERACTION, conditional=true) public boolean addCrossProfileWidgetProvider(@Nullable android.content.ComponentName, String);
     method public int addOverrideApn(@NonNull android.content.ComponentName, @NonNull android.telephony.data.ApnSetting);
     method @RequiresPermission(value=android.Manifest.permission.MANAGE_DEVICE_POLICY_LOCK_TASK, conditional=true) public void addPersistentPreferredActivity(@Nullable android.content.ComponentName, android.content.IntentFilter, @NonNull android.content.ComponentName);
-    method public void addUserRestriction(@NonNull android.content.ComponentName, String);
+    method public void addUserRestriction(@Nullable android.content.ComponentName, String);
     method public void addUserRestrictionGlobally(@NonNull String);
     method public boolean bindDeviceAdminServiceAsUser(@NonNull android.content.ComponentName, @NonNull android.content.Intent, @NonNull android.content.ServiceConnection, int, @NonNull android.os.UserHandle);
     method public boolean bindDeviceAdminServiceAsUser(@NonNull android.content.ComponentName, @NonNull android.content.Intent, @NonNull android.content.ServiceConnection, @NonNull android.content.Context.BindServiceFlags, @NonNull android.os.UserHandle);
@@ -8120,7 +8122,7 @@
     method @RequiresPermission(value=android.Manifest.permission.MANAGE_DEVICE_POLICY_LOCK_TASK, conditional=true) public void clearPackagePersistentPreferredActivities(@Nullable android.content.ComponentName, String);
     method @Deprecated public void clearProfileOwner(@NonNull android.content.ComponentName);
     method @RequiresPermission(value=android.Manifest.permission.MANAGE_DEVICE_POLICY_RESET_PASSWORD, conditional=true) public boolean clearResetPasswordToken(@Nullable android.content.ComponentName);
-    method public void clearUserRestriction(@NonNull android.content.ComponentName, String);
+    method public void clearUserRestriction(@Nullable android.content.ComponentName, String);
     method public android.content.Intent createAdminSupportIntent(@NonNull String);
     method @Nullable public android.os.UserHandle createAndManageUser(@NonNull android.content.ComponentName, @NonNull String, @NonNull android.content.ComponentName, @Nullable android.os.PersistableBundle, int);
     method public void enableSystemApp(@NonNull android.content.ComponentName, String);
@@ -8181,7 +8183,7 @@
     method @Deprecated @ColorInt public int getOrganizationColor(@NonNull android.content.ComponentName);
     method @Nullable @RequiresPermission(value=android.Manifest.permission.MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY, conditional=true) public CharSequence getOrganizationName(@Nullable android.content.ComponentName);
     method public java.util.List<android.telephony.data.ApnSetting> getOverrideApns(@NonNull android.content.ComponentName);
-    method @NonNull public android.app.admin.DevicePolicyManager getParentProfileInstance(@NonNull android.content.ComponentName);
+    method @NonNull public android.app.admin.DevicePolicyManager getParentProfileInstance(@Nullable android.content.ComponentName);
     method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS, android.Manifest.permission.REQUEST_PASSWORD_COMPLEXITY}, conditional=true) public int getPasswordComplexity();
     method public long getPasswordExpiration(@Nullable android.content.ComponentName);
     method public long getPasswordExpirationTimeout(@Nullable android.content.ComponentName);
@@ -8891,8 +8893,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 android.os.OutcomeReceiver<android.app.appfunctions.ExecuteAppFunctionResponse,android.app.appfunctions.AppFunctionException>);
-    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 @RequiresPermission(anyOf={"android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED", android.Manifest.permission.EXECUTE_APP_FUNCTIONS}, conditional=true) public void executeAppFunction(@NonNull android.app.appfunctions.ExecuteAppFunctionRequest, @NonNull java.util.concurrent.Executor, @NonNull android.os.CancellationSignal, @NonNull android.os.OutcomeReceiver<android.app.appfunctions.ExecuteAppFunctionResponse,android.app.appfunctions.AppFunctionException>);
+    method @RequiresPermission(anyOf={"android.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
@@ -10065,6 +10067,7 @@
     field @RequiresPermission(android.Manifest.permission.REQUEST_COMPANION_PROFILE_COMPUTER) public static final String DEVICE_PROFILE_COMPUTER = "android.app.role.COMPANION_DEVICE_COMPUTER";
     field @RequiresPermission(android.Manifest.permission.REQUEST_COMPANION_PROFILE_GLASSES) public static final String DEVICE_PROFILE_GLASSES = "android.app.role.COMPANION_DEVICE_GLASSES";
     field @RequiresPermission(android.Manifest.permission.REQUEST_COMPANION_PROFILE_NEARBY_DEVICE_STREAMING) public static final String DEVICE_PROFILE_NEARBY_DEVICE_STREAMING = "android.app.role.COMPANION_DEVICE_NEARBY_DEVICE_STREAMING";
+    field @FlaggedApi("android.companion.virtualdevice.flags.enable_limited_vdm_role") @RequiresPermission(android.Manifest.permission.REQUEST_COMPANION_PROFILE_SENSOR_DEVICE_STREAMING) public static final String DEVICE_PROFILE_SENSOR_DEVICE_STREAMING = "android.app.role.COMPANION_DEVICE_SENSOR_DEVICE_STREAMING";
     field public static final String DEVICE_PROFILE_WATCH = "android.app.role.COMPANION_DEVICE_WATCH";
   }
 
@@ -13380,6 +13383,7 @@
     method @NonNull public abstract android.graphics.drawable.Drawable getUserBadgedDrawableForDensity(@NonNull android.graphics.drawable.Drawable, @NonNull android.os.UserHandle, @Nullable android.graphics.Rect, int);
     method @NonNull public abstract android.graphics.drawable.Drawable getUserBadgedIcon(@NonNull android.graphics.drawable.Drawable, @NonNull android.os.UserHandle);
     method @NonNull public abstract CharSequence getUserBadgedLabel(@NonNull CharSequence, @NonNull android.os.UserHandle);
+    method @FlaggedApi("android.content.pm.cloud_compilation_pm") @NonNull public static android.content.pm.SigningInfo getVerifiedSigningInfo(@NonNull String, int) throws android.content.pm.SigningInfoException;
     method @NonNull @RequiresPermission(value="android.permission.WHITELIST_RESTRICTED_PERMISSIONS", conditional=true) public java.util.Set<java.lang.String> getWhitelistedRestrictedPermissions(@NonNull String, int);
     method @Nullable public abstract android.content.res.XmlResourceParser getXml(@NonNull String, @XmlRes int, @Nullable android.content.pm.ApplicationInfo);
     method public boolean hasSigningCertificate(@NonNull String, @NonNull byte[], int);
@@ -14025,8 +14029,17 @@
     method public android.content.pm.Signature[] getSigningCertificateHistory();
     method public boolean hasMultipleSigners();
     method public boolean hasPastSigningCertificates();
+    method @FlaggedApi("android.content.pm.cloud_compilation_pm") public boolean signersMatchExactly(@NonNull android.content.pm.SigningInfo);
     method public void writeToParcel(android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.SigningInfo> CREATOR;
+    field @FlaggedApi("android.content.pm.cloud_compilation_pm") public static final int VERSION_JAR = 1; // 0x1
+    field @FlaggedApi("android.content.pm.cloud_compilation_pm") public static final int VERSION_SIGNING_BLOCK_V2 = 2; // 0x2
+    field @FlaggedApi("android.content.pm.cloud_compilation_pm") public static final int VERSION_SIGNING_BLOCK_V3 = 3; // 0x3
+    field @FlaggedApi("android.content.pm.cloud_compilation_pm") public static final int VERSION_SIGNING_BLOCK_V4 = 4; // 0x4
+  }
+
+  @FlaggedApi("android.content.pm.cloud_compilation_pm") public class SigningInfoException extends java.lang.Exception {
+    method @FlaggedApi("android.content.pm.cloud_compilation_pm") public int getCode();
   }
 
   public final class VersionedPackage implements android.os.Parcelable {
@@ -24928,6 +24941,7 @@
     method @Nullable public android.net.Uri getIconUri();
     method @NonNull public String getId();
     method @NonNull public CharSequence getName();
+    method @FlaggedApi("com.android.media.flags.enable_media_route_2_info_provider_package_name") @Nullable public String getProviderPackageName();
     method @FlaggedApi("com.android.media.flags.enable_route_visibility_control_api") @NonNull public java.util.List<java.util.Set<java.lang.String>> getRequiredPermissions();
     method @FlaggedApi("com.android.media.flags.enable_built_in_speaker_route_suitability_statuses") public int getSuitabilityStatus();
     method @FlaggedApi("com.android.media.flags.enable_mirroring_in_media_router_2") public int getSupportedRoutingTypes();
@@ -45059,6 +45073,7 @@
     field @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") public static final String KEY_SATELLITE_ENTITLEMENT_STATUS_REFRESH_DAYS_INT = "satellite_entitlement_status_refresh_days_int";
     field @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") public static final String KEY_SATELLITE_ENTITLEMENT_SUPPORTED_BOOL = "satellite_entitlement_supported_bool";
     field @FlaggedApi("com.android.internal.telephony.flags.carrier_roaming_nb_iot_ntn") public static final String KEY_SATELLITE_ESOS_SUPPORTED_BOOL = "satellite_esos_supported_bool";
+    field @FlaggedApi("com.android.internal.telephony.flags.satellite_25q4_apis") public static final String KEY_SATELLITE_IGNORE_DATA_ROAMING_SETTING_BOOL = "satellite_ignore_data_roaming_setting_bool";
     field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final String KEY_SATELLITE_INFORMATION_REDIRECT_URL_STRING = "satellite_information_redirect_url_string";
     field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final String KEY_SATELLITE_NIDD_APN_NAME_STRING = "satellite_nidd_apn_name_string";
     field @FlaggedApi("com.android.internal.telephony.flags.carrier_roaming_nb_iot_ntn") public static final String KEY_SATELLITE_ROAMING_ESOS_INACTIVITY_TIMEOUT_SEC_INT = "satellite_roaming_esos_inactivity_timeout_sec_int";
@@ -47463,7 +47478,6 @@
     field public static final long NETWORK_TYPE_BITMASK_IWLAN = 131072L; // 0x20000L
     field public static final long NETWORK_TYPE_BITMASK_LTE = 4096L; // 0x1000L
     field @Deprecated public static final long NETWORK_TYPE_BITMASK_LTE_CA = 262144L; // 0x40000L
-    field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final long NETWORK_TYPE_BITMASK_NB_IOT_NTN = 1048576L; // 0x100000L
     field public static final long NETWORK_TYPE_BITMASK_NR = 524288L; // 0x80000L
     field public static final long NETWORK_TYPE_BITMASK_TD_SCDMA = 65536L; // 0x10000L
     field public static final long NETWORK_TYPE_BITMASK_UMTS = 4L; // 0x4L
@@ -47483,7 +47497,6 @@
     field @Deprecated public static final int NETWORK_TYPE_IDEN = 11; // 0xb
     field public static final int NETWORK_TYPE_IWLAN = 18; // 0x12
     field public static final int NETWORK_TYPE_LTE = 13; // 0xd
-    field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final int NETWORK_TYPE_NB_IOT_NTN = 21; // 0x15
     field public static final int NETWORK_TYPE_NR = 20; // 0x14
     field public static final int NETWORK_TYPE_TD_SCDMA = 17; // 0x11
     field public static final int NETWORK_TYPE_UMTS = 3; // 0x3
@@ -49356,7 +49369,7 @@
     method public static void dumpSpans(CharSequence, android.util.Printer, String);
     method public static CharSequence ellipsize(CharSequence, android.text.TextPaint, float, android.text.TextUtils.TruncateAt);
     method public static CharSequence ellipsize(CharSequence, android.text.TextPaint, float, android.text.TextUtils.TruncateAt, boolean, @Nullable android.text.TextUtils.EllipsizeCallback);
-    method public static boolean equals(CharSequence, CharSequence);
+    method public static boolean equals(@Nullable CharSequence, @Nullable CharSequence);
     method public static CharSequence expandTemplate(CharSequence, java.lang.CharSequence...);
     method public static int getCapsMode(CharSequence, int, int);
     method public static void getChars(CharSequence, int, int, char[], int);
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index bca2ce2..40069aa 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -130,7 +130,6 @@
 
   public abstract class PackageManager {
     method @NonNull public String getSdkSandboxPackageName();
-    method @FlaggedApi("android.content.pm.cloud_compilation_pm") @NonNull public static android.content.pm.SigningInfo getVerifiedSigningInfo(@NonNull String, int) throws android.content.pm.SigningInfoException;
     method @RequiresPermission(android.Manifest.permission.MAKE_UID_VISIBLE) public void makeUidVisible(int, int);
     field public static final String EXTRA_VERIFICATION_ROOT_HASH = "android.content.pm.extra.VERIFICATION_ROOT_HASH";
     field public static final int MATCH_STATIC_SHARED_AND_SDK_LIBRARIES = 67108864; // 0x4000000
@@ -141,18 +140,6 @@
     method @NonNull public String getPackageName();
   }
 
-  public final class SigningInfo implements android.os.Parcelable {
-    method @FlaggedApi("android.content.pm.cloud_compilation_pm") public boolean signersMatchExactly(@NonNull android.content.pm.SigningInfo);
-    field @FlaggedApi("android.content.pm.cloud_compilation_pm") public static final int VERSION_JAR = 1; // 0x1
-    field @FlaggedApi("android.content.pm.cloud_compilation_pm") public static final int VERSION_SIGNING_BLOCK_V2 = 2; // 0x2
-    field @FlaggedApi("android.content.pm.cloud_compilation_pm") public static final int VERSION_SIGNING_BLOCK_V3 = 3; // 0x3
-    field @FlaggedApi("android.content.pm.cloud_compilation_pm") public static final int VERSION_SIGNING_BLOCK_V4 = 4; // 0x4
-  }
-
-  @FlaggedApi("android.content.pm.cloud_compilation_pm") public class SigningInfoException extends java.lang.Exception {
-    method @FlaggedApi("android.content.pm.cloud_compilation_pm") public int getCode();
-  }
-
 }
 
 package android.hardware.usb {
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 9590e1a..0286c10 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -150,7 +150,6 @@
     field @FlaggedApi("com.android.window.flags.untrusted_embedding_any_app_permission") public static final String EMBED_ANY_APP_IN_UNTRUSTED_MODE = "android.permission.EMBED_ANY_APP_IN_UNTRUSTED_MODE";
     field @FlaggedApi("android.content.pm.emergency_install_permission") public static final String EMERGENCY_INSTALL_PACKAGES = "android.permission.EMERGENCY_INSTALL_PACKAGES";
     field public static final String ENTER_CAR_MODE_PRIORITIZED = "android.permission.ENTER_CAR_MODE_PRIORITIZED";
-    field @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") public static final String EXECUTE_APP_FUNCTIONS = "android.permission.EXECUTE_APP_FUNCTIONS";
     field @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") public static final String EXECUTE_APP_FUNCTIONS_TRUSTED = "android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED";
     field public static final String EXEMPT_FROM_AUDIO_RECORD_RESTRICTIONS = "android.permission.EXEMPT_FROM_AUDIO_RECORD_RESTRICTIONS";
     field public static final String FORCE_BACK = "android.permission.FORCE_BACK";
@@ -520,7 +519,9 @@
     field public static final int config_defaultCallScreening = 17039398; // 0x1040026
     field public static final int config_defaultDialer = 17039395; // 0x1040023
     field public static final int config_defaultNotes = 17039429; // 0x1040045
-    field @FlaggedApi("android.permission.flags.cross_user_role_platform_api_enabled") public static final int config_defaultReservedForTestingProfileGroupExclusivity;
+    field @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence_module") public static final int config_defaultOnDeviceIntelligenceDeviceConfigNamespace;
+    field @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence_module") public static final int config_defaultOnDeviceIntelligenceService;
+    field @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence_module") public static final int config_defaultOnDeviceSandboxedInferenceService;
     field @FlaggedApi("android.permission.flags.retail_demo_role_enabled") public static final int config_defaultRetailDemo = 17039432; // 0x1040048
     field public static final int config_defaultSms = 17039396; // 0x1040024
     field @FlaggedApi("android.permission.flags.wallet_role_enabled") public static final int config_defaultWallet = 17039433; // 0x1040049
@@ -5088,6 +5089,7 @@
     field public static final int REASON_ENDPOINT_STOPPED = 6; // 0x6
     field public static final int REASON_FAILURE = 0; // 0x0
     field public static final int REASON_OPEN_ENDPOINT_SESSION_REQUEST_REJECTED = 3; // 0x3
+    field public static final int REASON_PERMISSION_DENIED = 9; // 0x9
   }
 
   public static final class HubEndpoint.Builder {
@@ -10973,8 +10975,8 @@
     method @FlaggedApi("android.nfc.enable_nfc_mainline") public void setDynamicAidGroup(@NonNull android.nfc.cardemulation.AidGroup);
     method @FlaggedApi("android.nfc.enable_nfc_mainline") public void setOffHostSecureElement(@NonNull String);
     method @FlaggedApi("android.nfc.nfc_observe_mode") public void setShouldDefaultToObserveMode(boolean);
-    method @FlaggedApi("android.nfc.nfc_associated_role_services") public boolean shareRolePriority();
     method @FlaggedApi("android.nfc.nfc_observe_mode") public boolean shouldDefaultToObserveMode();
+    method @FlaggedApi("android.nfc.nfc_associated_role_services") public boolean wantsRoleHolderPriority();
     method @FlaggedApi("android.nfc.enable_nfc_mainline") public void writeToParcel(@NonNull android.os.Parcel, int);
     field @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public static final android.os.Parcelable.Creator<android.nfc.cardemulation.ApduServiceInfo> CREATOR;
     field @FlaggedApi("android.permission.flags.wallet_role_icon_property_enabled") public static final String PROPERTY_WALLET_PREFERRED_BANNER_AND_LABEL = "android.nfc.cardemulation.PROPERTY_WALLET_PREFERRED_BANNER_AND_LABEL";
@@ -18699,7 +18701,6 @@
     method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public void requestIsSupported(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>);
     method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestNtnSignalStrength(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.telephony.satellite.NtnSignalStrength,android.telephony.satellite.SatelliteManager.SatelliteException>);
     method @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestSatelliteAccessConfigurationForCurrentLocation(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.telephony.satellite.SatelliteAccessConfiguration,android.telephony.satellite.SatelliteManager.SatelliteException>);
-    method @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestSatelliteDisplayName(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.CharSequence,android.telephony.satellite.SatelliteManager.SatelliteException>);
     method @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestSatelliteSubscriberProvisionStatus(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.util.List<android.telephony.satellite.SatelliteSubscriberProvisionStatus>,android.telephony.satellite.SatelliteManager.SatelliteException>);
     method @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestSelectedNbIotSatelliteSubscriptionId(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Integer,android.telephony.satellite.SatelliteManager.SatelliteException>);
     method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestTimeForNextSatelliteVisibility(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.time.Duration,android.telephony.satellite.SatelliteManager.SatelliteException>);
@@ -19314,6 +19315,7 @@
     method @Deprecated public abstract void setUseWebViewBackgroundForOverscrollBackground(boolean);
     method @Deprecated public abstract void setUserAgent(int);
     method public abstract void setVideoOverlayForEmbeddedEncryptedVideoEnabled(boolean);
+    field @FlaggedApi("android.webkit.enable_chips") public static final long ENABLE_CHIPS = 380890146L; // 0x16b3ec22L
     field public static final long ENABLE_SIMPLIFIED_DARK_MODE = 214741472L; // 0xcccb1e0L
     field @FlaggedApi("android.webkit.user_agent_reduction") public static final long ENABLE_USER_AGENT_REDUCTION = 371034303L; // 0x161d88bfL
   }
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 8594cae..5171e68 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -2431,6 +2431,12 @@
     method @NonNull public static byte[] digest(@NonNull java.io.InputStream, @NonNull String) throws java.io.IOException, java.security.NoSuchAlgorithmException;
   }
 
+  public class Handler {
+    method @FlaggedApi("android.os.mainline_vcn_platform_api") public final boolean hasMessagesOrCallbacks();
+    method @FlaggedApi("android.os.mainline_vcn_platform_api") public final void removeCallbacksAndEqualMessages(@Nullable Object);
+    method @FlaggedApi("android.os.mainline_vcn_platform_api") public final void removeEqualMessages(int, @Nullable Object);
+  }
+
   public class IpcDataCache<Query, Result> extends android.app.PropertyInvalidatedCache<Query,Result> {
     ctor public IpcDataCache(int, @NonNull String, @NonNull String, @NonNull String, @NonNull android.os.IpcDataCache.QueryHandler<Query,Result>);
     method public static void disableForCurrentProcess(@NonNull String);
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 03ef669..fee8cdb 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -5782,8 +5782,7 @@
         }
 
         if (!getAttributionSource().getRenouncedPermissions().isEmpty()) {
-            final int permissionCount = permissions.length;
-            for (int i = 0; i < permissionCount; i++) {
+            for (int i = 0; i < permissions.length; i++) {
                 if (getAttributionSource().getRenouncedPermissions().contains(permissions[i])) {
                     throw new IllegalArgumentException("Cannot request renounced permission: "
                             + permissions[i]);
@@ -5791,13 +5790,55 @@
             }
         }
 
-        PackageManager packageManager = getDeviceId() == deviceId ? getPackageManager()
-                : createDeviceContext(deviceId).getPackageManager();
+        final Context context = getDeviceId() == deviceId ? this : createDeviceContext(deviceId);
+        if (Flags.permissionRequestShortCircuitEnabled()) {
+            int[] permissionsState = getPermissionRequestStates(context, permissions);
+            boolean hasRequestablePermission = false;
+            for (int i = 0; i < permissionsState.length; i++) {
+                if (permissionsState[i] == Context.PERMISSION_REQUEST_STATE_REQUESTABLE) {
+                    hasRequestablePermission = true;
+                    break;
+                }
+            }
+            // If none of the permissions is requestable, finish the request here.
+            if (!hasRequestablePermission) {
+                mHasCurrentPermissionsRequest = true;
+                Log.v(TAG, "No requestable permission in the request.");
+                int[] results = new int[permissionsState.length];
+                for (int i = 0; i < permissionsState.length; i++) {
+                    if (permissionsState[i] == Context.PERMISSION_REQUEST_STATE_GRANTED) {
+                        results[i] = PackageManager.PERMISSION_GRANTED;
+                    } else {
+                        results[i] = PackageManager.PERMISSION_DENIED;
+                    }
+                }
+                // Currently permission request result is passed to the client app asynchronously
+                // in onRequestPermissionsResult, lets keep async behavior here as well.
+                mHandler.post(() -> {
+                    mHasCurrentPermissionsRequest = false;
+                    onRequestPermissionsResult(requestCode, permissions, results, deviceId);
+                });
+                return;
+            }
+        }
+
+        final PackageManager packageManager = context.getPackageManager();
         final Intent intent = packageManager.buildRequestPermissionsIntent(permissions);
         startActivityForResult(REQUEST_PERMISSIONS_WHO_PREFIX, intent, requestCode, null);
         mHasCurrentPermissionsRequest = true;
     }
 
+    @NonNull
+    private int[] getPermissionRequestStates(@NonNull Context deviceContext,
+            @NonNull String[] permissions) {
+        final int size = permissions.length;
+        int[] results = new int[size];
+        for (int i = 0; i < size; i++) {
+            results[i] = deviceContext.getPermissionRequestState(permissions[i]);
+        }
+        return results;
+    }
+
     /**
      * Callback for the result from requesting permissions. This method
      * is invoked for every call on {@link #requestPermissions}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 48b74f2..717a2ac 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -1975,8 +1975,12 @@
 
         @Override
         public void dumpCacheInfo(ParcelFileDescriptor pfd, String[] args) {
-            PropertyInvalidatedCache.dumpCacheInfo(pfd, args);
-            IoUtils.closeQuietly(pfd);
+            try {
+                PropertyInvalidatedCache.dumpCacheInfo(pfd, args);
+                BroadcastStickyCache.dumpCacheInfo(pfd);
+            } finally {
+                IoUtils.closeQuietly(pfd);
+            }
         }
 
         private File getDatabasesDir(Context context) {
@@ -7094,12 +7098,9 @@
             System.runFinalization();
             System.gc();
         }
-        if (dhd.dumpBitmaps != null) {
-            Bitmap.dumpAll(dhd.dumpBitmaps);
-        }
         try (ParcelFileDescriptor fd = dhd.fd) {
             if (dhd.managed) {
-                Debug.dumpHprofData(dhd.path, fd.getFileDescriptor());
+                Debug.dumpHprofData(dhd.path, fd.getFileDescriptor(), dhd.dumpBitmaps);
             } else if (dhd.mallocInfo) {
                 Debug.dumpNativeMallocInfo(fd.getFileDescriptor());
             } else {
@@ -7124,9 +7125,6 @@
         if (dhd.finishCallback != null) {
             dhd.finishCallback.sendResult(null);
         }
-        if (dhd.dumpBitmaps != null) {
-            Bitmap.dumpAll(null); // clear dump
-        }
     }
 
     final void handleDispatchPackageBroadcast(int cmd, String[] packages) {
diff --git a/core/java/android/app/BroadcastStickyCache.java b/core/java/android/app/BroadcastStickyCache.java
index 1f62779..0ffbf2c 100644
--- a/core/java/android/app/BroadcastStickyCache.java
+++ b/core/java/android/app/BroadcastStickyCache.java
@@ -30,14 +30,20 @@
 import android.net.wifi.p2p.WifiP2pManager;
 import android.os.IpcDataCache;
 import android.os.IpcDataCache.Config;
+import android.os.ParcelFileDescriptor;
 import android.os.UpdateLock;
 import android.telephony.TelephonyManager;
 import android.util.ArrayMap;
+import android.util.IndentingPrintWriter;
 import android.view.WindowManagerPolicyConstants;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.FastPrintWriter;
+
+import java.io.FileOutputStream;
+import java.io.PrintWriter;
 
 /** @hide */
 public class BroadcastStickyCache {
@@ -221,6 +227,43 @@
         return sActionConfigMap.get(action);
     }
 
+    public static void dumpCacheInfo(@NonNull ParcelFileDescriptor pfd) {
+        if (!Flags.useStickyBcastCache()) {
+            return;
+        }
+        final PrintWriter pw = new FastPrintWriter(new FileOutputStream(pfd.getFileDescriptor()));
+        synchronized (BroadcastStickyCache.class) {
+            dumpCacheLocked(pw);
+        }
+        pw.flush();
+    }
+
+    @GuardedBy("BroadcastStickyCache.class")
+    private static void dumpCacheLocked(@NonNull PrintWriter pw) {
+        final IndentingPrintWriter ipw = new IndentingPrintWriter(
+                pw, "  " /* singleIndent */, "  " /* prefix */);
+        ipw.println("Cached sticky broadcasts:");
+        ipw.increaseIndent();
+        final int count = sFilterCacheMap.size();
+        if (count == 0) {
+            ipw.println("<empty>");
+        } else {
+            for (int i = 0; i < count; ++i) {
+                final StickyBroadcastFilter stickyBroadcast = sFilterCacheMap.keyAt(i);
+                final IpcDataCache<Void, Intent> ipcDataCache = sFilterCacheMap.valueAt(i);
+                ipw.print("Entry #");
+                ipw.print(i);
+                ipw.println(":");
+                ipw.increaseIndent();
+                ipw.print("action", stickyBroadcast.action).println();
+                ipw.print("filter", stickyBroadcast.filter.toLongString()).println();
+                ipcDataCache.dumpCacheEntries(pw);
+                ipw.decreaseIndent();
+            }
+        }
+        ipw.decreaseIndent();
+    }
+
     @VisibleForTesting
     private record StickyBroadcastFilter(@NonNull IntentFilter filter, @NonNull String action) {
     }
diff --git a/core/java/android/app/CameraCompatTaskInfo.java b/core/java/android/app/CameraCompatTaskInfo.java
index 845d2ac..aff6b35 100644
--- a/core/java/android/app/CameraCompatTaskInfo.java
+++ b/core/java/android/app/CameraCompatTaskInfo.java
@@ -36,36 +36,42 @@
  */
 public class CameraCompatTaskInfo implements Parcelable {
     /**
+     * Undefined camera compat mode.
+     */
+    public static final int CAMERA_COMPAT_FREEFORM_UNSPECIFIED = 0;
+
+    /**
      * The value to use when no camera compat treatment should be applied to a windowed task.
      */
-    public static final int CAMERA_COMPAT_FREEFORM_NONE = 0;
+    public static final int CAMERA_COMPAT_FREEFORM_NONE = 1;
 
     /**
      * The value to use when camera compat treatment should be applied to an activity requesting
      * portrait orientation, while a device is in landscape. Applies only to freeform tasks.
      */
-    public static final int CAMERA_COMPAT_FREEFORM_PORTRAIT_DEVICE_IN_LANDSCAPE = 1;
+    public static final int CAMERA_COMPAT_FREEFORM_PORTRAIT_DEVICE_IN_LANDSCAPE = 2;
 
     /**
      * The value to use when camera compat treatment should be applied to an activity requesting
      * landscape orientation, while a device is in landscape. Applies only to freeform tasks.
      */
-    public static final int CAMERA_COMPAT_FREEFORM_LANDSCAPE_DEVICE_IN_LANDSCAPE = 2;
+    public static final int CAMERA_COMPAT_FREEFORM_LANDSCAPE_DEVICE_IN_LANDSCAPE = 3;
 
     /**
      * The value to use when camera compat treatment should be applied to an activity requesting
      * portrait orientation, while a device is in portrait. Applies only to freeform tasks.
      */
-    public static final int CAMERA_COMPAT_FREEFORM_PORTRAIT_DEVICE_IN_PORTRAIT = 3;
+    public static final int CAMERA_COMPAT_FREEFORM_PORTRAIT_DEVICE_IN_PORTRAIT = 4;
 
     /**
      * The value to use when camera compat treatment should be applied to an activity requesting
      * landscape orientation, while a device is in portrait. Applies only to freeform tasks.
      */
-    public static final int CAMERA_COMPAT_FREEFORM_LANDSCAPE_DEVICE_IN_PORTRAIT = 4;
+    public static final int CAMERA_COMPAT_FREEFORM_LANDSCAPE_DEVICE_IN_PORTRAIT = 5;
 
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(prefix = { "CAMERA_COMPAT_FREEFORM_" }, value = {
+            CAMERA_COMPAT_FREEFORM_UNSPECIFIED,
             CAMERA_COMPAT_FREEFORM_NONE,
             CAMERA_COMPAT_FREEFORM_PORTRAIT_DEVICE_IN_LANDSCAPE,
             CAMERA_COMPAT_FREEFORM_LANDSCAPE_DEVICE_IN_LANDSCAPE,
@@ -184,6 +190,7 @@
     public static String freeformCameraCompatModeToString(
             @FreeformCameraCompatMode int freeformCameraCompatMode) {
         return switch (freeformCameraCompatMode) {
+            case CAMERA_COMPAT_FREEFORM_UNSPECIFIED -> "undefined";
             case CAMERA_COMPAT_FREEFORM_NONE -> "inactive";
             case CAMERA_COMPAT_FREEFORM_PORTRAIT_DEVICE_IN_LANDSCAPE ->
                     "app-portrait-device-landscape";
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index dcbdc23..d8aa8b3 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -2366,7 +2366,11 @@
             Log.v(TAG, "Treating renounced permission " + permission + " as denied");
             return PERMISSION_DENIED;
         }
+        int deviceId = resolveDeviceIdForPermissionCheck(permission);
+        return PermissionManager.checkPermission(permission, pid, uid, deviceId);
+    }
 
+    private int resolveDeviceIdForPermissionCheck(String permission) {
         // When checking a device-aware permission on a remote device, if the permission is CAMERA
         // or RECORD_AUDIO we need to check remote device's corresponding capability. If the remote
         // device doesn't have capability fall back to checking permission on the default device.
@@ -2387,9 +2391,9 @@
                 VirtualDevice virtualDevice = virtualDeviceManager.getVirtualDevice(deviceId);
                 if (virtualDevice != null) {
                     if ((Objects.equals(permission, Manifest.permission.RECORD_AUDIO)
-                                    && !virtualDevice.hasCustomAudioInputSupport())
+                            && !virtualDevice.hasCustomAudioInputSupport())
                             || (Objects.equals(permission, Manifest.permission.CAMERA)
-                                    && !virtualDevice.hasCustomCameraSupport())) {
+                            && !virtualDevice.hasCustomCameraSupport())) {
                         deviceId = Context.DEVICE_ID_DEFAULT;
                     }
                 } else {
@@ -2400,8 +2404,7 @@
                 }
             }
         }
-
-        return PermissionManager.checkPermission(permission, pid, uid, deviceId);
+        return deviceId;
     }
 
     /** @hide */
@@ -2503,6 +2506,16 @@
                 message);
     }
 
+    /** @hide */
+    @Override
+    public int getPermissionRequestState(String permission) {
+        Objects.requireNonNull(permission, "Permission name can't be null");
+        int deviceId = resolveDeviceIdForPermissionCheck(permission);
+        PermissionManager permissionManager = getSystemService(PermissionManager.class);
+        return permissionManager.getPermissionRequestState(getOpPackageName(), permission,
+                deviceId);
+    }
+
     @Override
     public void grantUriPermission(String toPackage, Uri uri, int modeFlags) {
          try {
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index aa2ada5..17638ee 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -3323,6 +3323,18 @@
     }
 
     /**
+     * Make sure this String is safe to put into a bundle.
+     * @hide
+     */
+    public static String safeString(String str) {
+        if (str == null) return str;
+        if (str.length() > MAX_CHARSEQUENCE_LENGTH) {
+            str = str.substring(0, MAX_CHARSEQUENCE_LENGTH);
+        }
+        return str;
+    }
+
+    /**
      * Make sure this CharSequence is safe to put into a bundle, which basically
      * means it had better not be some custom Parcelable implementation.
      * @hide
@@ -5051,7 +5063,7 @@
         @FlaggedApi(Flags.FLAG_API_RICH_ONGOING)
         @NonNull
         public Builder setShortCriticalText(@Nullable String shortCriticalText) {
-            mN.extras.putString(EXTRA_SHORT_CRITICAL_TEXT, shortCriticalText);
+            mN.extras.putString(EXTRA_SHORT_CRITICAL_TEXT, safeString(shortCriticalText));
             return this;
         }
 
diff --git a/core/java/android/app/PropertyInvalidatedCache.java b/core/java/android/app/PropertyInvalidatedCache.java
index 1e971a5..5567c08 100644
--- a/core/java/android/app/PropertyInvalidatedCache.java
+++ b/core/java/android/app/PropertyInvalidatedCache.java
@@ -17,13 +17,13 @@
 package android.app;
 
 import static android.text.TextUtils.formatSimple;
+
 import static com.android.internal.util.Preconditions.checkArgumentPositive;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.TestApi;
 import android.os.Binder;
-import android.os.Build;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
@@ -1356,7 +1356,7 @@
             @Nullable QueryHandler<Query, Result> computer) {
         mPropertyName = createPropertyName(args.mModule, args.mApi);
         mCacheName = cacheName;
-        mCacheNullResults = args.mCacheNulls && Flags.picCacheNulls();
+        mCacheNullResults = args.mCacheNulls;
         mNonce = getNonceHandler(mPropertyName);
         mMaxEntries = args.mMaxEntries;
         mCache = new CacheMap<>(args.mIsolateUids, args.mTestMode);
@@ -2326,6 +2326,19 @@
     }
 
     /**
+     * This dumps the detailed entries (Query and Result) inside the current instance of the
+     * {@link PropertyInvalidatedCache}.
+     *
+     * @param pw The PrintWriter object for the output stream.
+     * @hide
+     */
+    public void dumpCacheEntries(@NonNull PrintWriter pw) {
+        synchronized (mLock) {
+            mCache.dumpDetailed(pw);
+        }
+    }
+
+    /**
      * Nonces in shared memory are supported by a string block that acts as a table of contents
      * for nonce names, and an array of nonce values.  There are two key design principles with
      * respect to nonce maps:
diff --git a/core/java/android/app/TEST_MAPPING b/core/java/android/app/TEST_MAPPING
index 637187e..e93d8bdb 100644
--- a/core/java/android/app/TEST_MAPPING
+++ b/core/java/android/app/TEST_MAPPING
@@ -157,6 +157,10 @@
             "file_patterns": ["(/|^)ContextImpl.java"]
         },
         {
+            "name": "BroadcastUnitTests",
+            "file_patterns": ["(/|^)BroadcastStickyCache.java"]
+        },
+        {
             "file_patterns": [
                 "(/|^)Activity.*.java",
                 "(/|^)PendingIntent.java",
@@ -177,10 +181,6 @@
         {
             "file_patterns": ["(/|^)AppOpsManager.java"],
             "name": "CtsAppOpsTestCases"
-        },
-        {
-            "file_patterns": ["(/|^)BroadcastStickyCache.java"],
-            "name": "BroadcastUnitTests"
         }
     ]
 }
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index 89e25e7..360376d 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -2730,6 +2730,7 @@
      * @param allowBackup {@code true} if the OS is permitted to back up this wallpaper
      *                    image for restore to a future device; {@code false} otherwise.
      * @param which       Flags indicating which wallpaper(s) to configure with the new imagery.
+     * @return An integer ID assigned to the newly active wallpaper; or zero on failure.
      * @hide
      */
     @FlaggedApi(FLAG_LIVE_WALLPAPER_CONTENT_HANDLING)
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 9ddc729..39c27a1 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -12190,13 +12190,6 @@
      * be enforced device-wide. These constants will also state in their documentation which
      * permission is required to manage the restriction using this API.
      *
-     * <p>For callers targeting Android {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE} or
-     * above, calling this API will result in applying the restriction locally on the calling user,
-     * or locally on the parent profile if called from the
-     * {@link DevicePolicyManager} instance obtained from
-     * {@link #getParentProfileInstance(ComponentName)}. To set a restriction globally, call
-     * {@link #addUserRestrictionGlobally} instead.
-     *
      * <p>
      * Starting from {@link Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, after the user restriction
      * policy has been set, {@link PolicyUpdateReceiver#onPolicySetResult(Context, String,
@@ -12217,13 +12210,18 @@
      * same parameters as PolicyUpdateReceiver#onPolicySetResult and the {@link PolicyUpdateResult}
      * will contain the reason why the policy changed.
      *
-     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+     * @param admin Which {@link DeviceAdminReceiver} this request is associated with or
+     * {@code null} if the caller is not a device admin.
      * @param key   The key of the restriction.
      * @throws SecurityException if {@code admin} is not a device or profile owner and if the caller
      * has not been granted the permission to set the given user restriction.
      */
+    // NB: For permission-based callers using this API will result in applying the restriction
+    // locally on the calling user or locally on the parent profile if called from through parent
+    // instance. To set a restriction globally, call addUserRestrictionGlobally() instead.
+    // Permission-based callers must target Android U or above.
     @SupportsCoexistence
-    public void addUserRestriction(@NonNull ComponentName admin,
+    public void addUserRestriction(@Nullable ComponentName admin,
             @UserManager.UserRestrictionKey String key) {
         if (mService != null) {
             try {
@@ -12358,10 +12356,6 @@
      * constants state in their documentation which permission is required to manage the restriction
      * using this API.
      *
-     * <p>For callers targeting Android {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE} or
-     * above, calling this API will result in clearing any local and global restriction with the
-     * specified key that was previously set by the caller.
-     *
      * <p>
      * Starting from {@link Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, after the user restriction
      * policy has been cleared, {@link PolicyUpdateReceiver#onPolicySetResult(Context, String,
@@ -12382,13 +12376,17 @@
      * same parameters as PolicyUpdateReceiver#onPolicySetResult and the {@link PolicyUpdateResult}
      * will contain the reason why the policy changed.
      *
-     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+     * @param admin Which {@link DeviceAdminReceiver} this request is associated with or
+     * {@code null} if the caller is not a device admin.
      * @param key   The key of the restriction.
      * @throws SecurityException if {@code admin} is not a device or profile owner  and if the
      *  caller has not been granted the permission to set the given user restriction.
      */
+    // NB: For permission-based callers using this API will result in clearing any local and global
+    // restriction with the specified key that was previously set by the caller.
+    // Permission-based callers must target Android U or above.
     @SupportsCoexistence
-    public void clearUserRestriction(@NonNull ComponentName admin,
+    public void clearUserRestriction(@Nullable ComponentName admin,
             @UserManager.UserRestrictionKey String key) {
         if (mService != null) {
             try {
@@ -14556,8 +14554,8 @@
     }
 
     /**
-     * Called by the profile owner of a managed profile to obtain a {@link DevicePolicyManager}
-     * whose calls act on the parent profile.
+     * Called by the profile owner of a managed profile or other apps in a managed profile to
+     * obtain a {@link DevicePolicyManager} whose calls act on the parent profile.
      *
      * <p>The following methods are supported for the parent instance, all other methods will
      * throw a SecurityException when called on the parent instance:
@@ -14614,10 +14612,12 @@
      * <li>{@link #wipeData}</li>
      * </ul>
      *
+     * @param admin Which {@link DeviceAdminReceiver} this request is associated with or
+     *         {@code null} if the caller is not a profile owner.
      * @return a new instance of {@link DevicePolicyManager} that acts on the parent profile.
-     * @throws SecurityException if {@code admin} is not a profile owner.
+     * @throws SecurityException if the current user is not a managed profile.
      */
-    public @NonNull DevicePolicyManager getParentProfileInstance(@NonNull ComponentName admin) {
+    public @NonNull DevicePolicyManager getParentProfileInstance(@Nullable ComponentName admin) {
         throwIfParentInstance("getParentProfileInstance");
         UserManager um = mContext.getSystemService(UserManager.class);
         if (!um.isManagedProfile()) {
diff --git a/core/java/android/app/appfunctions/SafeOneTimeExecuteAppFunctionCallback.java b/core/java/android/app/appfunctions/SafeOneTimeExecuteAppFunctionCallback.java
index e290169..e527de2 100644
--- a/core/java/android/app/appfunctions/SafeOneTimeExecuteAppFunctionCallback.java
+++ b/core/java/android/app/appfunctions/SafeOneTimeExecuteAppFunctionCallback.java
@@ -19,10 +19,12 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.os.RemoteException;
+import android.os.SystemClock;
 import android.util.Log;
 
 import java.util.Objects;
 import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
 
 /**
  * A wrapper of IExecuteAppFunctionCallback which swallows the {@link RemoteException}. This
@@ -38,7 +40,10 @@
 
     @NonNull private final IExecuteAppFunctionCallback mCallback;
 
-    @Nullable CompletionCallback mCompletionCallback;
+    @Nullable
+    private final CompletionCallback mCompletionCallback;
+
+    private final AtomicLong mExecutionStartTimeAfterBindMillis = new AtomicLong();
 
     public SafeOneTimeExecuteAppFunctionCallback(@NonNull IExecuteAppFunctionCallback callback) {
         this(callback, /* completionCallback= */ null);
@@ -59,7 +64,8 @@
         try {
             mCallback.onSuccess(result);
             if (mCompletionCallback != null) {
-                mCompletionCallback.finalizeOnSuccess(result);
+                mCompletionCallback.finalizeOnSuccess(result,
+                        mExecutionStartTimeAfterBindMillis.get());
             }
         } catch (RemoteException ex) {
             // Failed to notify the other end. Ignore.
@@ -76,7 +82,8 @@
         try {
             mCallback.onError(error);
             if (mCompletionCallback != null) {
-                mCompletionCallback.finalizeOnError(error);
+                mCompletionCallback.finalizeOnError(error,
+                        mExecutionStartTimeAfterBindMillis.get());
             }
         } catch (RemoteException ex) {
             // Failed to notify the other end. Ignore.
@@ -93,14 +100,25 @@
     }
 
     /**
+     * Sets the execution start time of the request. Used to calculate the overhead latency of
+     * requests.
+     */
+    public void setExecutionStartTimeMillis() {
+        if (!mExecutionStartTimeAfterBindMillis.compareAndSet(0, SystemClock.elapsedRealtime())) {
+            Log.w(TAG, "Ignore subsequent calls to setExecutionStartTimeMillis()");
+        }
+    }
+
+    /**
      * Provides a hook to execute additional actions after the {@link IExecuteAppFunctionCallback}
      * has been invoked.
      */
     public interface CompletionCallback {
         /** Called after {@link IExecuteAppFunctionCallback#onSuccess}. */
-        void finalizeOnSuccess(@NonNull ExecuteAppFunctionResponse result);
+        void finalizeOnSuccess(@NonNull ExecuteAppFunctionResponse result,
+                long executionStartTimeMillis);
 
         /** Called after {@link IExecuteAppFunctionCallback#onError}. */
-        void finalizeOnError(@NonNull AppFunctionException error);
+        void finalizeOnError(@NonNull AppFunctionException error, long executionStartTimeMillis);
     }
 }
diff --git a/core/java/android/app/compat/ChangeIdStateCache.java b/core/java/android/app/compat/ChangeIdStateCache.java
index 7d21cbf..258ce06 100644
--- a/core/java/android/app/compat/ChangeIdStateCache.java
+++ b/core/java/android/app/compat/ChangeIdStateCache.java
@@ -16,8 +16,6 @@
 
 package android.app.compat;
 
-import static android.app.PropertyInvalidatedCache.createSystemCacheKey;
-
 import android.annotation.NonNull;
 import android.app.PropertyInvalidatedCache;
 import android.content.Context;
@@ -34,7 +32,10 @@
 @android.ravenwood.annotation.RavenwoodKeepWholeClass
 public final class ChangeIdStateCache
         extends PropertyInvalidatedCache<ChangeIdStateQuery, Boolean> {
-    private static final String CACHE_KEY = createSystemCacheKey("is_compat_change_enabled");
+
+    private static final String CACHE_MODULE = PropertyInvalidatedCache.MODULE_SYSTEM;
+    private static final String CACHE_API = "is_compat_change_enabled";
+
     private static final int MAX_ENTRIES = 2048;
     private static boolean sDisabled = getDefaultDisabled();
     private volatile IPlatformCompat mPlatformCompat;
@@ -51,7 +52,12 @@
 
     /** @hide */
     public ChangeIdStateCache() {
-        super(MAX_ENTRIES, CACHE_KEY);
+        super(new PropertyInvalidatedCache.Args(CACHE_MODULE)
+                .maxEntries(MAX_ENTRIES)
+                .isolateUids(false)
+                .cacheNulls(false)
+                .api(CACHE_API),
+                CACHE_API, null);
     }
 
     /**
@@ -72,7 +78,7 @@
      */
     public static void invalidate() {
         if (!sDisabled) {
-            PropertyInvalidatedCache.invalidateCache(CACHE_KEY);
+            PropertyInvalidatedCache.invalidateCache(CACHE_MODULE, CACHE_API);
         }
     }
 
diff --git a/core/java/android/app/supervision/SupervisionManager.java b/core/java/android/app/supervision/SupervisionManager.java
index aee1cd9..a5b58f9 100644
--- a/core/java/android/app/supervision/SupervisionManager.java
+++ b/core/java/android/app/supervision/SupervisionManager.java
@@ -16,8 +16,10 @@
 
 package android.app.supervision;
 
+import android.annotation.RequiresPermission;
 import android.annotation.SystemService;
 import android.annotation.UserHandleAware;
+import android.annotation.UserIdInt;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.os.RemoteException;
@@ -32,9 +34,7 @@
     private final Context mContext;
     private final ISupervisionManager mService;
 
-    /**
-     * @hide
-     */
+    /** @hide */
     @UnsupportedAppUsage
     public SupervisionManager(Context context, ISupervisionManager service) {
         mContext = context;
@@ -48,8 +48,23 @@
      */
     @UserHandleAware
     public boolean isSupervisionEnabled() {
+        return isSupervisionEnabledForUser(mContext.getUserId());
+    }
+
+    /**
+     * Returns whether the device is supervised.
+     *
+     * <p>The caller must be from the same user as the target or hold the {@link
+     * android.Manifest.permission#INTERACT_ACROSS_USERS} permission.
+     *
+     * @hide
+     */
+    @RequiresPermission(
+            value = android.Manifest.permission.INTERACT_ACROSS_USERS,
+            conditional = true)
+    public boolean isSupervisionEnabledForUser(@UserIdInt int userId) {
         try {
-            return mService.isSupervisionEnabledForUser(mContext.getUserId());
+            return mService.isSupervisionEnabledForUser(userId);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/app/wallpaper/WallpaperDescription.java b/core/java/android/app/wallpaper/WallpaperDescription.java
index ca2d9e6..999a5da 100644
--- a/core/java/android/app/wallpaper/WallpaperDescription.java
+++ b/core/java/android/app/wallpaper/WallpaperDescription.java
@@ -118,14 +118,16 @@
     }
 
     /**
-     * @return the title for this wallpaper, with each list element intended to be a separate
-     * line, or {@code null} if not provided
+     * @return the title for this wallpaper, or {@code null} if not provided
      */
     @Nullable public CharSequence getTitle() {
         return mTitle;
     }
 
-    /** @return the description for this wallpaper */
+    /**
+     * @return the description for this wallpaper, with each list element intended to be shown on a
+     * separate line in the UI
+     */
     @NonNull
     public List<CharSequence> getDescription() {
         return mDescription;
diff --git a/core/java/android/companion/AssociationRequest.java b/core/java/android/companion/AssociationRequest.java
index f368935..32cbf32 100644
--- a/core/java/android/companion/AssociationRequest.java
+++ b/core/java/android/companion/AssociationRequest.java
@@ -90,9 +90,9 @@
     public static final String DEVICE_PROFILE_GLASSES = "android.app.role.COMPANION_DEVICE_GLASSES";
 
     /**
-     * Device profile: a virtual display capable of rendering Android applications, and sending back
+     * Device profile: a virtual device capable of rendering Android applications, and sending back
      * input events.
-     *
+     * <p>
      * Only applications that have been granted
      * {@link android.Manifest.permission#REQUEST_COMPANION_PROFILE_APP_STREAMING} are allowed to
      * request to be associated with such devices.
@@ -106,7 +106,7 @@
     /**
      * Device profile: a virtual device capable of rendering content from an Android host to a
      * nearby device.
-     *
+     * <p>
      * Only applications that have been granted
      * {@link android.Manifest.permission#REQUEST_COMPANION_PROFILE_NEARBY_DEVICE_STREAMING}
      * are allowed to request to be associated with such devices.
@@ -118,6 +118,21 @@
             "android.app.role.COMPANION_DEVICE_NEARBY_DEVICE_STREAMING";
 
     /**
+     * Device profile: a virtual device capable of streaming sensor data such as camera, audio and
+     * IMU between an Android host and a nearby device.
+     * <p>
+     * Only applications that have been granted
+     * {@link android.Manifest.permission#REQUEST_COMPANION_PROFILE_SENSOR_DEVICE_STREAMING}
+     * are allowed to request to be associated with such devices.
+     *
+     * @see AssociationRequest.Builder#setDeviceProfile
+     */
+    @FlaggedApi(android.companion.virtualdevice.flags.Flags.FLAG_ENABLE_LIMITED_VDM_ROLE)
+    @RequiresPermission(Manifest.permission.REQUEST_COMPANION_PROFILE_SENSOR_DEVICE_STREAMING)
+    public static final String DEVICE_PROFILE_SENSOR_DEVICE_STREAMING =
+            "android.app.role.COMPANION_DEVICE_SENSOR_DEVICE_STREAMING";
+
+    /**
      * Device profile: Android Automotive Projection
      *
      * Only applications that have been granted
diff --git a/core/java/android/companion/virtualnative/IVirtualDeviceManagerNative.aidl b/core/java/android/companion/virtualnative/IVirtualDeviceManagerNative.aidl
index 5a13255..78f8ab4 100644
--- a/core/java/android/companion/virtualnative/IVirtualDeviceManagerNative.aidl
+++ b/core/java/android/companion/virtualnative/IVirtualDeviceManagerNative.aidl
@@ -64,4 +64,9 @@
      * Returns the device policy for the given virtual device and policy type.
      */
     int getDevicePolicy(int deviceId, int policyType);
+
+    /**
+     * Returns the ID of the device which owns the display with the given ID.
+     */
+    int getDeviceIdForDisplayId(int displayId);
 }
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 6ec6a62..7abf560 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -788,6 +788,40 @@
     public static final int RECEIVER_NOT_EXPORTED = 0x4;
 
     /**
+     * The permission is granted.
+     *
+     * @hide
+     */
+    public static final int PERMISSION_REQUEST_STATE_GRANTED = 0;
+
+    /**
+     * The permission isn't granted, but apps can request the permission. When the app request
+     * the permission, user will be prompted with permission dialog to grant or deny the request.
+     *
+     * @hide
+     */
+    public static final int PERMISSION_REQUEST_STATE_REQUESTABLE = 1;
+
+    /**
+     * The permission is denied, and shouldn't be requested by apps. Permission request
+     * will be automatically denied by the system, preventing the permission dialog from being
+     * displayed to the user.
+     *
+     * @hide
+     */
+    public static final int PERMISSION_REQUEST_STATE_UNREQUESTABLE = 2;
+
+
+    /** @hide */
+    @IntDef(prefix = { "PERMISSION_REQUEST_STATE_" }, value = {
+            PERMISSION_REQUEST_STATE_GRANTED,
+            PERMISSION_REQUEST_STATE_REQUESTABLE,
+            PERMISSION_REQUEST_STATE_UNREQUESTABLE
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface PermissionRequestState {}
+
+    /**
      * Returns an AssetManager instance for the application's package.
      * <p>
      * <strong>Note:</strong> Implementations of this method should return
@@ -6989,6 +7023,31 @@
             @NonNull @PermissionName String permission, @Nullable String message);
 
     /**
+     * Returns the permission request state for a given runtime permission. This method provides a
+     * streamlined mechanism for applications to determine whether a permission can be
+     * requested (i.e. whether the user will be prompted with a permission dialog).
+     *
+     * <p>Traditionally, determining if a permission has been permanently denied (unrequestable)
+     * required applications to initiate a permission request and subsequently analyze the result
+     * of {@link android.app.Activity#shouldShowRequestPermissionRationale} in conjunction with the
+     * grant result within the {@link android.app.Activity#onRequestPermissionsResult} callback.
+     *
+     * @param permission The name of the permission.
+     *
+     * @return The current request state of the specified permission, represented by one of the
+     * following constants: {@link PermissionRequestState#PERMISSION_REQUEST_STATE_GRANTED},
+     * {@link PermissionRequestState#PERMISSION_REQUEST_STATE_REQUESTABLE}, or
+     * {@link PermissionRequestState#PERMISSION_REQUEST_STATE_UNREQUESTABLE}.
+     *
+     * @hide
+     */
+    @CheckResult
+    @PermissionRequestState
+    public int getPermissionRequestState(@NonNull String permission) {
+        throw new RuntimeException("Not implemented. Must override in a subclass.");
+    }
+
+    /**
      * Grant permission to access a specific Uri to another package, regardless
      * of whether that package has general permission to access the Uri's
      * content provider.  This can be used to grant specific, temporary
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 413eb98..a146807 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -1012,6 +1012,12 @@
         mBase.enforceCallingOrSelfPermission(permission, message);
     }
 
+    /** @hide */
+    @Override
+    public int getPermissionRequestState(String permission) {
+        return mBase.getPermissionRequestState(permission);
+    }
+
     @Override
     public void grantUriPermission(String toPackage, Uri uri, int modeFlags) {
         mBase.grantUriPermission(toPackage, uri, modeFlags);
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 7e08051..438a21b 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -3151,16 +3151,6 @@
     public static final long MAXIMUM_VERIFICATION_TIMEOUT = 60*60*1000;
 
     /**
-     * As the generated feature count is useful for classes that may not be compiled in the same
-     * annotation processing unit as PackageManager, we redeclare it here for visibility.
-     *
-     * @hide
-     */
-    @VisibleForTesting
-    public static final int SDK_FEATURE_COUNT =
-            com.android.internal.pm.SystemFeaturesMetadata.SDK_FEATURE_COUNT;
-
-    /**
      * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: The device's
      * audio pipeline is low-latency, more suitable for audio applications sensitive to delays or
      * lag in sound input or output.
@@ -12023,11 +12013,8 @@
      * file.
      *
      * @throws SigningInfoException if the verification fails
-     *
-     * @hide
      */
     @FlaggedApi(android.content.pm.Flags.FLAG_CLOUD_COMPILATION_PM)
-    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
     public static @NonNull SigningInfo getVerifiedSigningInfo(@NonNull String path,
             @AppSigningSchemeVersion int minAppSigningSchemeVersion) throws SigningInfoException {
         ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
@@ -12039,4 +12026,28 @@
         }
         return new SigningInfo(result.getResult());
     }
+
+    /**
+     * As the generated feature count is useful for classes that may not be compiled in the same
+     * annotation processing unit as PackageManager, we redeclare it here for visibility.
+     *
+     * @hide
+     */
+    @VisibleForTesting
+    public static final int SDK_FEATURE_COUNT =
+            com.android.internal.pm.SystemFeaturesMetadata.SDK_FEATURE_COUNT;
+
+    /**
+     * Returns a stable index for PackageManager-defined features.
+     *
+     * <p> Similar to {@link #SDK_FEATURE_COUNT}, we redeclare this utility method generated by the
+     * annotation processor for internal visibility.
+     *
+     * @return index in [0, {@link #SDK_FEATURECOUNT}) for PackageManager-defined features, else -1.
+     * @hide
+     */
+    @VisibleForTesting
+    public static int maybeGetSdkFeatureIndex(String featureName) {
+        return com.android.internal.pm.SystemFeaturesMetadata.maybeGetSdkFeatureIndex(featureName);
+    }
 }
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 4b579e7..219b204 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -2322,10 +2322,10 @@
 
             } else if (tagName.equals(TAG_ADOPT_PERMISSIONS)) {
                 sa = res.obtainAttributes(parser,
-                        com.android.internal.R.styleable.AndroidManifestOriginalPackage);
+                        com.android.internal.R.styleable.AndroidManifestAdoptPermissions);
 
                 String name = sa.getNonConfigurationString(
-                        com.android.internal.R.styleable.AndroidManifestOriginalPackage_name, 0);
+                        com.android.internal.R.styleable.AndroidManifestAdoptPermissions_name, 0);
 
                 sa.recycle();
 
diff --git a/core/java/android/content/pm/SigningInfo.java b/core/java/android/content/pm/SigningInfo.java
index e4fbd1f..21bbb0a 100644
--- a/core/java/android/content/pm/SigningInfo.java
+++ b/core/java/android/content/pm/SigningInfo.java
@@ -22,7 +22,6 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.SystemApi;
 import android.content.pm.SigningDetails.SignatureSchemeVersion;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -40,41 +39,29 @@
     /**
      * JAR signing (v1 scheme).
      * See https://source.android.com/docs/security/features/apksigning#v1.
-     *
-     * @hide
      */
     @FlaggedApi(Flags.FLAG_CLOUD_COMPILATION_PM)
-    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
     public static final int VERSION_JAR = SignatureSchemeVersion.JAR;
 
     /**
      * APK signature scheme v2.
      * See https://source.android.com/docs/security/features/apksigning/v2.
-     *
-     * @hide
      */
     @FlaggedApi(Flags.FLAG_CLOUD_COMPILATION_PM)
-    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
     public static final int VERSION_SIGNING_BLOCK_V2 = SignatureSchemeVersion.SIGNING_BLOCK_V2;
 
     /**
      * APK signature scheme v3.
      * See https://source.android.com/docs/security/features/apksigning/v3.
-     *
-     * @hide
      */
     @FlaggedApi(Flags.FLAG_CLOUD_COMPILATION_PM)
-    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
     public static final int VERSION_SIGNING_BLOCK_V3 = SignatureSchemeVersion.SIGNING_BLOCK_V3;
 
     /**
      * APK signature scheme v4.
      * See https://source.android.com/docs/security/features/apksigning/v4.
-     *
-     * @hide
      */
     @FlaggedApi(Flags.FLAG_CLOUD_COMPILATION_PM)
-    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
     public static final int VERSION_SIGNING_BLOCK_V4 = SignatureSchemeVersion.SIGNING_BLOCK_V4;
 
     /** @hide */
@@ -255,11 +242,8 @@
 
     /**
      * Returns true if the signing certificates in this and other match exactly.
-     *
-     * @hide
      */
     @FlaggedApi(Flags.FLAG_CLOUD_COMPILATION_PM)
-    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
     public boolean signersMatchExactly(@NonNull SigningInfo other) {
         return mSigningDetails.signaturesMatchExactly(other.mSigningDetails);
     }
diff --git a/core/java/android/content/pm/SigningInfoException.java b/core/java/android/content/pm/SigningInfoException.java
index a81e07e..2fd1bfb 100644
--- a/core/java/android/content/pm/SigningInfoException.java
+++ b/core/java/android/content/pm/SigningInfoException.java
@@ -19,17 +19,13 @@
 import android.annotation.FlaggedApi;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.SystemApi;
 
 /**
  * Indicates an error when verifying the
  * <a href="https://source.android.com/docs/security/features/apksigning">app signing</a>
  * information.
- *
- * @hide
  */
 @FlaggedApi(Flags.FLAG_CLOUD_COMPILATION_PM)
-@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
 public class SigningInfoException extends Exception {
     private final int mCode;
 
diff --git a/core/java/android/content/pm/flags.aconfig b/core/java/android/content/pm/flags.aconfig
index dfeee2a..0d219a9 100644
--- a/core/java/android/content/pm/flags.aconfig
+++ b/core/java/android/content/pm/flags.aconfig
@@ -149,6 +149,13 @@
 }
 
 flag {
+    name: "cache_sdk_system_features"
+    namespace: "system_performance"
+    description: "Feature flag to enable optimized cache for SDK-defined system feature lookups."
+    bug: "375000483"
+}
+
+flag {
     name: "provide_info_of_apk_in_apex"
     is_exported: true
     namespace: "package_manager_service"
@@ -362,3 +369,9 @@
     is_fixed_read_only: true
 }
 
+flag {
+    name: "remove_hidden_module_usage"
+    namespace: "modularization"
+    description: "Feature flag to remove the consumption of the hidden module status (ModuleInfo#IsHidden) in the Android source tree."
+    bug: "363952383"
+}
diff --git a/core/java/android/database/sqlite/SQLiteRawStatement.java b/core/java/android/database/sqlite/SQLiteRawStatement.java
index 3f3e46b..ce2334a 100644
--- a/core/java/android/database/sqlite/SQLiteRawStatement.java
+++ b/core/java/android/database/sqlite/SQLiteRawStatement.java
@@ -533,11 +533,11 @@
     }
 
     /**
-     * Return the number of columns in the current result row.
+     * Return the number of columns in the result set for the statement.
      *
      * @see <a href="http://sqlite.org/c3ref/column_count.html">sqlite3_column_count</a>
      *
-     * @return The number of columns in the result row.
+     * @return The number of columns in the result set.
      * @throws IllegalStateException if the statement is closed or this is a foreign thread.
      */
     public int getResultColumnCount() {
diff --git a/core/java/android/hardware/contexthub/HubEndpoint.java b/core/java/android/hardware/contexthub/HubEndpoint.java
index 1f12bbf..99f331f 100644
--- a/core/java/android/hardware/contexthub/HubEndpoint.java
+++ b/core/java/android/hardware/contexthub/HubEndpoint.java
@@ -66,13 +66,14 @@
                 REASON_CLOSE_ENDPOINT_SESSION_REQUESTED,
                 REASON_ENDPOINT_INVALID,
                 REASON_ENDPOINT_STOPPED,
+                REASON_PERMISSION_DENIED,
             })
     public @interface Reason {}
 
     /** Unclassified failure */
     public static final int REASON_FAILURE = 0;
 
-    // The values 1 and 2 are reserved at the Context Hub HAL but not exposed to apps.
+    // The values 1-2 are reserved at the Context Hub HAL but not exposed to apps.
 
     /** The peer rejected the request to open this endpoint session. */
     public static final int REASON_OPEN_ENDPOINT_SESSION_REQUEST_REJECTED = 3;
@@ -83,6 +84,11 @@
     /** The peer endpoint is invalid. */
     public static final int REASON_ENDPOINT_INVALID = 5;
 
+    // The values 6-8 are reserved at the Context Hub HAL but not exposed to apps.
+
+    /** The endpoint did not have the required permissions. */
+    public static final int REASON_PERMISSION_DENIED = 9;
+
     /**
      * The endpoint is now stopped. The app should retrieve the endpoint info using {@link
      * android.hardware.location.ContextHubManager#findEndpoints} or register updates through
@@ -349,7 +355,10 @@
         }
         try {
             IContextHubEndpoint serviceToken =
-                    service.registerEndpoint(mPendingHubEndpointInfo, mServiceCallback);
+                    service.registerEndpoint(
+                            mPendingHubEndpointInfo,
+                            mServiceCallback,
+                            mPendingHubEndpointInfo.getTag());
             mAssignedHubEndpointInfo = serviceToken.getAssignedHubEndpointInfo();
             mServiceToken = serviceToken;
         } catch (RemoteException e) {
@@ -395,11 +404,11 @@
 
         HubEndpointSession newSession;
         try {
-            // Request system service to assign session id.
-            int sessionId = mServiceToken.openSession(destinationInfo, serviceDescriptor);
-
-            // Save the newly created session
             synchronized (mLock) {
+                // Request system service to assign session id.
+                int sessionId = mServiceToken.openSession(destinationInfo, serviceDescriptor);
+
+                // Save the newly created session
                 newSession =
                         new HubEndpointSession(
                                 sessionId,
diff --git a/core/java/android/hardware/contexthub/HubServiceInfo.java b/core/java/android/hardware/contexthub/HubServiceInfo.java
index a1c52fb..2f33e8f 100644
--- a/core/java/android/hardware/contexthub/HubServiceInfo.java
+++ b/core/java/android/hardware/contexthub/HubServiceInfo.java
@@ -132,6 +132,21 @@
         return 0;
     }
 
+    @Override
+    public String toString() {
+        StringBuilder out = new StringBuilder();
+        out.append("Service: ");
+        out.append("descriptor=");
+        out.append(mServiceDescriptor);
+        out.append(", format=");
+        out.append(mFormat);
+        out.append(", version=");
+        out.append(Integer.toHexString(mMajorVersion));
+        out.append(".");
+        out.append(Integer.toHexString(mMinorVersion));
+        return out.toString();
+    }
+
     /** Parcel implementation details */
     @Override
     public void writeToParcel(@NonNull Parcel dest, int flags) {
diff --git a/core/java/android/hardware/contexthub/IContextHubEndpoint.aidl b/core/java/android/hardware/contexthub/IContextHubEndpoint.aidl
index b76b227..44f80c8 100644
--- a/core/java/android/hardware/contexthub/IContextHubEndpoint.aidl
+++ b/core/java/android/hardware/contexthub/IContextHubEndpoint.aidl
@@ -39,6 +39,7 @@
      * @throws IllegalArgumentException If the HubEndpointInfo is not valid.
      * @throws IllegalStateException If there are too many opened sessions.
      */
+    @EnforcePermission("ACCESS_CONTEXT_HUB")
     int openSession(in HubEndpointInfo destination, in @nullable String serviceDescriptor);
 
     /**
@@ -49,6 +50,7 @@
      *
      * @throws IllegalStateException If the session wasn't opened.
      */
+    @EnforcePermission("ACCESS_CONTEXT_HUB")
     void closeSession(int sessionId, int reason);
 
     /**
@@ -60,11 +62,13 @@
      *
      * @throws IllegalStateException If the session wasn't opened.
      */
+    @EnforcePermission("ACCESS_CONTEXT_HUB")
     void openSessionRequestComplete(int sessionId);
 
     /**
      * Unregister this endpoint from the HAL, invalidate the EndpointInfo previously assigned.
      */
+    @EnforcePermission("ACCESS_CONTEXT_HUB")
     void unregister();
 
     /**
@@ -76,6 +80,7 @@
      * @param transactionCallback Nullable. If the hub message requires a reply, the transactionCallback
      *                            will be set to non-null.
      */
+    @EnforcePermission("ACCESS_CONTEXT_HUB")
     void sendMessage(int sessionId, in HubMessage message,
                      in @nullable IContextHubTransactionCallback transactionCallback);
 
@@ -87,5 +92,6 @@
      * @param messageSeqNumber The message sequence number, this should match a previously received HubMessage.
      * @param errorCode The message delivery status detail.
      */
+    @EnforcePermission("ACCESS_CONTEXT_HUB")
     void sendMessageDeliveryStatus(int sessionId, int messageSeqNumber, byte errorCode);
 }
diff --git a/core/java/android/hardware/input/KeyGestureEvent.java b/core/java/android/hardware/input/KeyGestureEvent.java
index af756b9..9f8505f 100644
--- a/core/java/android/hardware/input/KeyGestureEvent.java
+++ b/core/java/android/hardware/input/KeyGestureEvent.java
@@ -124,7 +124,11 @@
     public static final int KEY_GESTURE_TYPE_TOGGLE_MAGNIFICATION = 74;
     public static final int KEY_GESTURE_TYPE_ACTIVATE_SELECT_TO_SPEAK = 75;
     public static final int KEY_GESTURE_TYPE_MAXIMIZE_FREEFORM_WINDOW = 76;
-
+    public static final int KEY_GESTURE_TYPE_TOGGLE_DO_NOT_DISTURB = 77;
+    public static final int KEY_GESTURE_TYPE_MAGNIFIER_PAN_LEFT = 78;
+    public static final int KEY_GESTURE_TYPE_MAGNIFIER_PAN_RIGHT = 79;
+    public static final int KEY_GESTURE_TYPE_MAGNIFIER_PAN_UP = 80;
+    public static final int KEY_GESTURE_TYPE_MAGNIFIER_PAN_DOWN = 81;
 
     public static final int FLAG_CANCELLED = 1;
 
@@ -215,7 +219,12 @@
             KEY_GESTURE_TYPE_MAGNIFIER_ZOOM_OUT,
             KEY_GESTURE_TYPE_TOGGLE_MAGNIFICATION,
             KEY_GESTURE_TYPE_ACTIVATE_SELECT_TO_SPEAK,
-            KEY_GESTURE_TYPE_MAXIMIZE_FREEFORM_WINDOW
+            KEY_GESTURE_TYPE_MAXIMIZE_FREEFORM_WINDOW,
+            KEY_GESTURE_TYPE_TOGGLE_DO_NOT_DISTURB,
+            KEY_GESTURE_TYPE_MAGNIFIER_PAN_LEFT,
+            KEY_GESTURE_TYPE_MAGNIFIER_PAN_RIGHT,
+            KEY_GESTURE_TYPE_MAGNIFIER_PAN_UP,
+            KEY_GESTURE_TYPE_MAGNIFIER_PAN_DOWN,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface KeyGestureType {
@@ -788,6 +797,16 @@
                 return "KEY_GESTURE_TYPE_ACTIVATE_SELECT_TO_SPEAK";
             case KEY_GESTURE_TYPE_MAXIMIZE_FREEFORM_WINDOW:
                 return "KEY_GESTURE_TYPE_MAXIMIZE_FREEFORM_WINDOW";
+            case KEY_GESTURE_TYPE_TOGGLE_DO_NOT_DISTURB:
+                return "KEY_GESTURE_TYPE_TOGGLE_DO_NOT_DISTURB";
+            case KEY_GESTURE_TYPE_MAGNIFIER_PAN_LEFT:
+                return "KEY_GESTURE_TYPE_MAGNIFIER_PAN_LEFT";
+            case KEY_GESTURE_TYPE_MAGNIFIER_PAN_RIGHT:
+                return "KEY_GESTURE_TYPE_MAGNIFIER_PAN_RIGHT";
+            case KEY_GESTURE_TYPE_MAGNIFIER_PAN_UP:
+                return "KEY_GESTURE_TYPE_MAGNIFIER_PAN_UP";
+            case KEY_GESTURE_TYPE_MAGNIFIER_PAN_DOWN:
+                return "KEY_GESTURE_TYPE_MAGNIFIER_PAN_DOWN";
             default:
                 return Integer.toHexString(value);
         }
diff --git a/core/java/android/hardware/input/input_framework.aconfig b/core/java/android/hardware/input/input_framework.aconfig
index aaa78aa..313bad5 100644
--- a/core/java/android/hardware/input/input_framework.aconfig
+++ b/core/java/android/hardware/input/input_framework.aconfig
@@ -196,4 +196,11 @@
     namespace: "input"
     description: "Allows the user to disable pointer acceleration for mouse and touchpads."
     bug: "349006858"
-}
\ No newline at end of file
+}
+
+flag {
+    name: "mouse_scrolling_acceleration"
+    namespace: "input"
+    description: "Allows the user to disable input scrolling acceleration for mouse."
+    bug: "383555305"
+}
diff --git a/core/java/android/hardware/location/ContextHubManager.java b/core/java/android/hardware/location/ContextHubManager.java
index d9888ad..1e0cc94 100644
--- a/core/java/android/hardware/location/ContextHubManager.java
+++ b/core/java/android/hardware/location/ContextHubManager.java
@@ -754,6 +754,7 @@
      * @param executor the executor to invoke callbacks for this client
      * @return the callback interface
      */
+    @FlaggedApi(Flags.FLAG_OFFLOAD_API)
     private IContextHubEndpointDiscoveryCallback createDiscoveryCallback(
             IHubEndpointDiscoveryCallback callback,
             Executor executor,
@@ -767,21 +768,9 @@
                 }
                 executor.execute(
                         () -> {
-                            // TODO(b/380293951): Refactor
                             List<HubDiscoveryInfo> discoveryList =
-                                    new ArrayList<>(hubEndpointInfoList.length);
-                            for (HubEndpointInfo info : hubEndpointInfoList) {
-                                if (serviceDescriptor != null) {
-                                    for (HubServiceInfo sInfo : info.getServiceInfoCollection()) {
-                                        if (sInfo.getServiceDescriptor()
-                                                .equals(serviceDescriptor)) {
-                                            discoveryList.add(new HubDiscoveryInfo(info, sInfo));
-                                        }
-                                    }
-                                } else {
-                                    discoveryList.add(new HubDiscoveryInfo(info));
-                                }
-                            }
+                                    getMatchingEndpointDiscoveryList(
+                                            hubEndpointInfoList, serviceDescriptor);
                             if (discoveryList.isEmpty()) {
                                 Log.w(TAG, "onEndpointsStarted: no matching service descriptor");
                             } else {
@@ -799,19 +788,8 @@
                 executor.execute(
                         () -> {
                             List<HubDiscoveryInfo> discoveryList =
-                                    new ArrayList<>(hubEndpointInfoList.length);
-                            for (HubEndpointInfo info : hubEndpointInfoList) {
-                                if (serviceDescriptor != null) {
-                                    for (HubServiceInfo sInfo : info.getServiceInfoCollection()) {
-                                        if (sInfo.getServiceDescriptor()
-                                                .equals(serviceDescriptor)) {
-                                            discoveryList.add(new HubDiscoveryInfo(info, sInfo));
-                                        }
-                                    }
-                                } else {
-                                    discoveryList.add(new HubDiscoveryInfo(info));
-                                }
-                            }
+                                    getMatchingEndpointDiscoveryList(
+                                            hubEndpointInfoList, serviceDescriptor);
                             if (discoveryList.isEmpty()) {
                                 Log.w(TAG, "onEndpointsStopped: no matching service descriptor");
                             } else {
@@ -823,6 +801,34 @@
     }
 
     /**
+     * Generates a list of matching endpoint discovery info, given the list and an (optional)
+     * service descriptor. If service descriptor is null, all endpoints are added to the filtered
+     * output list.
+     *
+     * @param hubEndpointInfoList The hub endpoints to filter.
+     * @param serviceDescriptor The optional service descriptor to match, null if adding all
+     *     endpoints.
+     * @return The list of filtered HubDiscoveryInfo which matches the serviceDescriptor.
+     */
+    @FlaggedApi(Flags.FLAG_OFFLOAD_API)
+    private List<HubDiscoveryInfo> getMatchingEndpointDiscoveryList(
+            HubEndpointInfo[] hubEndpointInfoList, @Nullable String serviceDescriptor) {
+        List<HubDiscoveryInfo> discoveryList = new ArrayList<>(hubEndpointInfoList.length);
+        for (HubEndpointInfo info : hubEndpointInfoList) {
+            if (serviceDescriptor != null) {
+                for (HubServiceInfo sInfo : info.getServiceInfoCollection()) {
+                    if (sInfo.getServiceDescriptor().equals(serviceDescriptor)) {
+                        discoveryList.add(new HubDiscoveryInfo(info, sInfo));
+                    }
+                }
+            } else {
+                discoveryList.add(new HubDiscoveryInfo(info));
+            }
+        }
+        return discoveryList;
+    }
+
+    /**
      * Equivalent to {@link #registerEndpointDiscoveryCallback(long, IHubEndpointDiscoveryCallback,
      * Executor)} with the default executor in the main thread.
      */
diff --git a/core/java/android/hardware/location/IContextHubService.aidl b/core/java/android/hardware/location/IContextHubService.aidl
index f14aadc..2a47237 100644
--- a/core/java/android/hardware/location/IContextHubService.aidl
+++ b/core/java/android/hardware/location/IContextHubService.aidl
@@ -137,7 +137,7 @@
 
     // Register an endpoint with the context hub
     @EnforcePermission("ACCESS_CONTEXT_HUB")
-    IContextHubEndpoint registerEndpoint(in HubEndpointInfo pendingEndpointInfo, in IContextHubEndpointCallback callback);
+    IContextHubEndpoint registerEndpoint(in HubEndpointInfo pendingEndpointInfo, in IContextHubEndpointCallback callback, String packageName);
 
     // Register an endpoint discovery callback (id)
     @EnforcePermission("ACCESS_CONTEXT_HUB")
diff --git a/core/java/android/net/flags.aconfig b/core/java/android/net/flags.aconfig
index 95b5f69..8d12b76 100644
--- a/core/java/android/net/flags.aconfig
+++ b/core/java/android/net/flags.aconfig
@@ -31,9 +31,9 @@
 }
 
 flag {
-  name: "x509_extensions_certificate_transparency"
+  name: "mdns_improvement_for_25q2"
   is_exported: true
-  namespace: "network_security"
-  description: "Flag to use checkServerTrusted to verify SCTs in OCSP and TLS Data"
-  bug: "319829948"
+  namespace: "android_core_networking"
+  description: "Flag for MDNS quality, reliability and performance improvement in 25Q2"
+  bug: "373270045"
 }
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 84ca5ed..d54dbad9 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -1558,6 +1558,7 @@
      * @hide
      */
     @SuppressWarnings("FlaggedApi") // SDK_INT_MULTIPLIER is defined in this file
+    @SuppressLint("InlinedApi")
     public static @SdkIntFull int parseFullVersion(@NonNull String version) {
         int index = version.indexOf('.');
         int major;
@@ -1569,12 +1570,22 @@
                 major = Integer.parseInt(version.substring(0, index));
                 minor = Integer.parseInt(version.substring(index + 1));
             }
-            if (major < 0 || minor < 0) {
-                throw new NumberFormatException();
+            if (major < 0) {
+                throw new NumberFormatException("negative major version");
+            }
+            if (major >= 21474) {
+                throw new NumberFormatException("major version too large, must be less than 21474");
+            }
+            if (minor < 0) {
+                throw new NumberFormatException("negative minor version");
+            }
+            if (minor >= VERSION_CODES_FULL.SDK_INT_MULTIPLIER) {
+                throw new NumberFormatException("minor version too large, must be less than "
+                        + VERSION_CODES_FULL.SDK_INT_MULTIPLIER);
             }
         } catch (NumberFormatException e) {
             throw new IllegalArgumentException("failed to parse '" + version
-                    + "' as a major.minor version code");
+                    + "' as a major.minor version code", e);
         }
         return major * VERSION_CODES_FULL.SDK_INT_MULTIPLIER + minor;
     }
diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java
index ef1e6c94..2bc6ab5 100644
--- a/core/java/android/os/Debug.java
+++ b/core/java/android/os/Debug.java
@@ -22,6 +22,7 @@
 import android.app.AppGlobals;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
+import android.graphics.Bitmap;
 import android.util.Log;
 
 import com.android.internal.util.FastPrintWriter;
@@ -2139,6 +2140,47 @@
     }
 
     /**
+     * Like dumpHprofData(String), but takes an argument of bitmapFormat,
+     * which can be png, jpg, webp, or null (no bitmaps in heapdump).
+     *
+     * @hide
+     */
+    public static void dumpHprofData(String fileName, String bitmapFormat)
+            throws IOException {
+        try {
+            if (bitmapFormat != null) {
+                Bitmap.dumpAll(bitmapFormat);
+            }
+            VMDebug.dumpHprofData(fileName);
+        } finally {
+            if (bitmapFormat != null) {
+                Bitmap.dumpAll(null); // clear dump data
+            }
+        }
+    }
+
+    /**
+     * Like dumpHprofData(String, FileDescriptor), but takes an argument
+     * of bitmapFormat, which can be png, jpg, webp, or null (no bitmaps
+     * in heapdump).
+     *
+     * @hide
+     */
+    public static void dumpHprofData(String fileName, FileDescriptor fd,
+            String bitmapFormat) throws IOException {
+        try {
+            if (bitmapFormat != null) {
+                Bitmap.dumpAll(bitmapFormat);
+            }
+            VMDebug.dumpHprofData(fileName, fd);
+        } finally {
+            if (bitmapFormat != null) {
+                Bitmap.dumpAll(null); // clear dump data
+            }
+        }
+    }
+
+    /**
      * Collect "hprof" and send it to DDMS.  This may cause a GC.
      *
      * @throws UnsupportedOperationException if the VM was built without
diff --git a/core/java/android/os/Handler.java b/core/java/android/os/Handler.java
index d0828c3..eaecd34 100644
--- a/core/java/android/os/Handler.java
+++ b/core/java/android/os/Handler.java
@@ -20,6 +20,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.util.Log;
 import android.util.Printer;
@@ -839,6 +840,7 @@
      *@hide
      */
     @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    @TestApi
     @FlaggedApi(android.os.Flags.FLAG_MAINLINE_VCN_PLATFORM_API)
     public final void removeEqualMessages(int what, @Nullable Object object) {
         mQueue.removeEqualMessages(this, what, disallowNullArgumentIfShared(object));
@@ -872,6 +874,7 @@
      *@hide
      */
     @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    @TestApi
     @FlaggedApi(android.os.Flags.FLAG_MAINLINE_VCN_PLATFORM_API)
     public final void removeCallbacksAndEqualMessages(@Nullable Object token) {
         mQueue.removeCallbacksAndEqualMessages(this, disallowNullArgumentIfShared(token));
@@ -889,6 +892,7 @@
      * @hide
      */
     @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    @TestApi
     @FlaggedApi(android.os.Flags.FLAG_MAINLINE_VCN_PLATFORM_API)
     public final boolean hasMessagesOrCallbacks() {
         return mQueue.hasMessages(this);
diff --git a/core/java/android/os/Looper.java b/core/java/android/os/Looper.java
index ddf2b61..0125905 100644
--- a/core/java/android/os/Looper.java
+++ b/core/java/android/os/Looper.java
@@ -332,16 +332,55 @@
         return -1;
     }
 
+    private static int getThreadGroup() {
+        int threadGroup = Process.THREAD_GROUP_DEFAULT;
+
+        if (!Process.isIsolated()) {
+            threadGroup = Process.getProcessGroup(Process.myTid());
+        }
+        return threadGroup;
+    }
+
+    private static String threadGroupToString(int threadGroup) {
+        switch (threadGroup) {
+            case Process.THREAD_GROUP_BACKGROUND:
+                return "BACKGROUND";
+            case Process.THREAD_GROUP_FOREGROUND:
+                return "FOREGROUND";
+            case Process.THREAD_GROUP_SYSTEM:
+                return "SYSTEM";
+            case Process.THREAD_GROUP_AUDIO_APP:
+                return "AUDIO_APP";
+            case Process.THREAD_GROUP_AUDIO_SYS:
+                return "AUDIO_SYS";
+            case Process.THREAD_GROUP_TOP_APP:
+                return "TOP_APP";
+            case Process.THREAD_GROUP_RT_APP:
+                return "RT_APP";
+            case Process.THREAD_GROUP_RESTRICTED:
+                return "RESTRICTED";
+            default:
+                return "UNKNOWN";
+        }
+    }
+
     private static boolean showSlowLog(long threshold, long measureStart, long measureEnd,
             String what, Message msg) {
         final long actualTime = measureEnd - measureStart;
         if (actualTime < threshold) {
             return false;
         }
+
+        String name = Process.myProcessName();
+        String threadGroup = threadGroupToString(getThreadGroup());
+        boolean isMain = myLooper() == getMainLooper();
+
         // For slow delivery, the current message isn't really important, but log it anyway.
         Slog.w(TAG, "Slow " + what + " took " + actualTime + "ms "
-                + Thread.currentThread().getName() + " h="
-                + msg.target.getClass().getName() + " c=" + msg.callback + " m=" + msg.what);
+                + Thread.currentThread().getName() + " app=" + name
+                + " main=" + isMain + " group=" + threadGroup
+                + " h=" + msg.target.getClass().getName() + " c=" + msg.callback
+                + " m=" + msg.what);
         return true;
     }
 
diff --git a/core/java/android/os/OWNERS b/core/java/android/os/OWNERS
index 8d35338..f3bb514 100644
--- a/core/java/android/os/OWNERS
+++ b/core/java/android/os/OWNERS
@@ -115,13 +115,20 @@
 # Performance
 per-file IpcDataCache.java = file:/PERFORMANCE_OWNERS
 
+# Processes, threads, and scheduling
+per-file Process.java = file:/PERFORMANCE_OWNERS
+
 # Memory
 per-file OomKillRecord.java = file:/MEMORY_OWNERS
 
 # MessageQueue and related classes
 per-file MessageQueue.java = mfasheh@google.com, shayba@google.com
 per-file Message.java = mfasheh@google.com, shayba@google.com
+per-file Looper.java = mfasheh@google.com, shayba@google.com
 per-file TestLooperManager.java = mfasheh@google.com, shayba@google.com
+per-file Handler.java = mfasheh@google.com, shayba@google.com
+per-file HandlerThread.java = mfasheh@google.com, shayba@google.com
+per-file HandlerExecutor.java = mfasheh@google.com, shayba@google.com
 
 # Stats
 per-file IStatsBootstrapAtomService.aidl = file:/services/core/java/com/android/server/stats/OWNERS
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index e728243..907d968 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -553,10 +553,9 @@
      * Foreground thread group - All threads in
      * this group are scheduled with a normal share of the CPU.
      * Value is same as constant SP_FOREGROUND of enum SchedPolicy.
-     * Not used at this level.
      * @hide
      **/
-    private static final int THREAD_GROUP_FOREGROUND = 1;
+    public static final int THREAD_GROUP_FOREGROUND = 1;
 
     /**
      * System thread group.
@@ -1315,19 +1314,6 @@
     }
 
     /**
-     * Adjust the swappiness level for a process.
-     *
-     * @param pid The process identifier to set.
-     * @param is_increased Whether swappiness should be increased or default.
-     *
-     * @return Returns true if the underlying system supports this
-     *         feature, else false.
-     *
-     * {@hide}
-     */
-    public static final native boolean setSwappiness(int pid, boolean is_increased);
-
-    /**
      * Change this process's argv[0] parameter.  This can be useful to show
      * more descriptive information in things like the 'ps' command.
      *
diff --git a/core/java/android/os/ServiceManagerNative.java b/core/java/android/os/ServiceManagerNative.java
index 49b696d..7ea521e 100644
--- a/core/java/android/os/ServiceManagerNative.java
+++ b/core/java/android/os/ServiceManagerNative.java
@@ -50,7 +50,8 @@
 class ServiceManagerProxy implements IServiceManager {
     public ServiceManagerProxy(IBinder remote) {
         mRemote = remote;
-        mServiceManager = IServiceManager.Stub.asInterface(this.getNativeServiceManager());
+        mServiceManager = IServiceManager.Stub.asInterface(
+            Binder.allowBlocking(this.getNativeServiceManager()));
     }
 
     public IBinder asBinder() {
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index b9f2cfc..132805d 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -4197,12 +4197,21 @@
             android.Manifest.permission.MANAGE_USERS,
             android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true)
     private boolean hasUserRestrictionForUser(@NonNull @UserRestrictionKey String restrictionKey,
-            @UserIdInt int userId) {
-        try {
-            return mService.hasUserRestriction(restrictionKey, userId);
-        } catch (RemoteException re) {
-            throw re.rethrowFromSystemServer();
-        }
+            @NonNull @UserIdInt int userId) {
+        return getUserRestrictionFromQuery(new Pair(restrictionKey, userId));
+    }
+
+    /** @hide */
+    @CachedProperty()
+    private boolean getUserRestrictionFromQuery(@NonNull Pair<String, Integer> restrictionPerUser) {
+        return UserManagerCache.getUserRestrictionFromQuery(
+                (Pair<String, Integer> q) -> mService.hasUserRestriction(q.first, q.second),
+                restrictionPerUser);
+    }
+
+    /** @hide */
+    public static final void invalidateUserRestriction() {
+        UserManagerCache.invalidateUserRestrictionFromQuery();
     }
 
     /**
@@ -6477,6 +6486,7 @@
             UserManagerCache.invalidateProfileParent();
         }
         invalidateEnabledProfileIds();
+        invalidateUserRestriction();
     }
 
     /**
diff --git a/core/java/android/os/health/OWNERS b/core/java/android/os/health/OWNERS
index 6045344..26fc8fa 100644
--- a/core/java/android/os/health/OWNERS
+++ b/core/java/android/os/health/OWNERS
@@ -2,3 +2,6 @@
 
 dplotnikov@google.com
 mwachens@google.com
+
+# for headroom API only
+xwxw@google.com
\ No newline at end of file
diff --git a/core/java/android/os/vibrator/persistence/VibrationXmlSerializer.java b/core/java/android/os/vibrator/persistence/VibrationXmlSerializer.java
index a95ce79..c7778de 100644
--- a/core/java/android/os/vibrator/persistence/VibrationXmlSerializer.java
+++ b/core/java/android/os/vibrator/persistence/VibrationXmlSerializer.java
@@ -22,7 +22,8 @@
 import android.os.VibrationEffect;
 import android.util.Xml;
 
-import com.android.internal.vibrator.persistence.VibrationEffectXmlSerializer;
+import com.android.internal.vibrator.persistence.LegacyVibrationEffectXmlSerializer;
+import com.android.internal.vibrator.persistence.VibrationEffectSerializer;
 import com.android.internal.vibrator.persistence.XmlConstants;
 import com.android.internal.vibrator.persistence.XmlSerializedVibration;
 import com.android.internal.vibrator.persistence.XmlSerializerException;
@@ -123,7 +124,13 @@
         }
 
         try {
-            serializedVibration = VibrationEffectXmlSerializer.serialize(effect, serializerFlags);
+            if (android.os.vibrator.Flags.normalizedPwleEffects()) {
+                serializedVibration = VibrationEffectSerializer.serialize(effect,
+                        serializerFlags);
+            } else {
+                serializedVibration = LegacyVibrationEffectXmlSerializer.serialize(effect,
+                        serializerFlags);
+            }
             XmlValidator.checkSerializedVibration(serializedVibration, effect);
         } catch (XmlSerializerException e) {
             // Serialization failed or created incomplete representation, fail before writing.
diff --git a/core/java/android/permission/IPermissionManager.aidl b/core/java/android/permission/IPermissionManager.aidl
index 55011e5..b375812 100644
--- a/core/java/android/permission/IPermissionManager.aidl
+++ b/core/java/android/permission/IPermissionManager.aidl
@@ -108,6 +108,8 @@
     int checkUidPermission(int uid, String permissionName, int deviceId);
 
     Map<String, PermissionState> getAllPermissionStates(String packageName, String persistentDeviceId, int userId);
+
+    int getPermissionRequestState(String packageName, String permissionName, int deviceId);
 }
 
 /**
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index 2473de4..bdf8d23 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -1742,6 +1742,16 @@
         }
     }
 
+    private static int getPermissionRequestStateUncached(String packageName, String permission,
+            int deviceId) {
+        try {
+            return AppGlobals.getPermissionManager().getPermissionRequestState(
+                    packageName, permission, deviceId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
     /**
      * Identifies a permission query.
      *
@@ -1795,6 +1805,46 @@
         }
     }
 
+    private static final class PermissionRequestStateQuery {
+        final String mPackageName;
+        final String mPermission;
+        final int mDeviceId;
+
+        PermissionRequestStateQuery(@NonNull String packageName, @NonNull String permission,
+                int deviceId) {
+            mPackageName = packageName;
+            mPermission = permission;
+            mDeviceId = deviceId;
+        }
+
+        @Override
+        public String toString() {
+            return TextUtils.formatSimple("PermissionRequestStateQuery(package=\"%s\","
+                            + " permission=\"%s\", " + "deviceId=%d)",
+                    mPackageName, mPermission, mDeviceId);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mPackageName, mPermission, mDeviceId);
+        }
+
+        @Override
+        public boolean equals(@Nullable Object rval) {
+            if (rval == null) {
+                return false;
+            }
+            PermissionRequestStateQuery other;
+            try {
+                other = (PermissionRequestStateQuery) rval;
+            } catch (ClassCastException ex) {
+                return false;
+            }
+            return mDeviceId == other.mDeviceId && Objects.equals(mPackageName, other.mPackageName)
+                    && Objects.equals(mPermission, other.mPermission);
+        }
+    }
+
     // The legacy system property "package_info" had two purposes: to invalidate PIC caches and to
     // signal that package information, and therefore permissions, might have changed.
     // AudioSystem is the only client of the signaling behavior.  The "separate permissions
@@ -1842,10 +1892,30 @@
             };
 
     /** @hide */
+    private static final PropertyInvalidatedCache<PermissionRequestStateQuery, Integer>
+            sPermissionRequestStateCache =
+            new PropertyInvalidatedCache<>(
+                    512, CACHE_KEY_PACKAGE_INFO_CACHE, "getPermissionRequestState") {
+                @Override
+                public Integer recompute(PermissionRequestStateQuery query) {
+                    return getPermissionRequestStateUncached(query.mPackageName, query.mPermission,
+                            query.mDeviceId);
+                }
+            };
+
+    /** @hide */
     public static int checkPermission(@Nullable String permission, int pid, int uid, int deviceId) {
         return sPermissionCache.query(new PermissionQuery(permission, pid, uid, deviceId));
     }
 
+    /** @hide */
+    @Context.PermissionRequestState
+    public int getPermissionRequestState(@NonNull String packageName, @NonNull String permission,
+            int deviceId) {
+        return sPermissionRequestStateCache.query(
+                new PermissionRequestStateQuery(packageName, permission, deviceId));
+    }
+
     /**
      * Gets the permission states for requested package and persistent device.
      * <p>
diff --git a/core/java/android/permission/flags.aconfig b/core/java/android/permission/flags.aconfig
index af96ccf..70d8891 100644
--- a/core/java/android/permission/flags.aconfig
+++ b/core/java/android/permission/flags.aconfig
@@ -340,7 +340,7 @@
     is_fixed_read_only: true
     namespace: "health_fitness_aconfig"
     description: "This flag protects the permission that is required to call Health Connect backup and restore apis"
-    bug: "376014879" # android_fr bug
+    bug: "324019102" # android_fr bug
     is_exported: true
 }
 
@@ -467,15 +467,6 @@
 }
 
 flag {
-    name: "cross_user_role_platform_api_enabled"
-    is_exported: true
-    is_fixed_read_only: true
-    namespace: "permissions"
-    description: "Enable cross-user roles platform API"
-    bug: "367732307"
-}
-
-flag {
     name: "rate_limit_batched_note_op_async_callbacks_enabled"
     is_fixed_read_only: true
     is_exported: true
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 4acb631..c3a4930 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -2473,7 +2473,7 @@
      * when a new SIM subscription has become available.
      * <p>
      * This Activity will only launch successfully if the newly active subscription ID is set as the
-     * value of {@link EXTRA_SUB_ID} and the value corresponds with an active SIM subscription.
+     * value of {@link #EXTRA_SUB_ID} and the value corresponds with an active SIM subscription.
      * <p>
      * Input: {@link #EXTRA_SUB_ID}: the subscription ID of the newly active SIM subscription.
      * <p>
@@ -8950,6 +8950,18 @@
                 "high_text_contrast_enabled";
 
         /**
+         * Setting that specifies the status of the High Contrast Text
+         * rectangle refresh's one-time prompt.
+         * 0 = UNKNOWN
+         * 1 = PROMPT_SHOWN
+         * 2 = PROMPT_UNNECESSARY
+         *
+         * @hide
+         */
+        public static final String ACCESSIBILITY_HCT_RECT_PROMPT_STATUS =
+                "accessibility_hct_rect_prompt_status";
+
+        /**
          * The color contrast, float in [-1, 1], 1 being the highest contrast.
          *
          * @hide
@@ -13710,6 +13722,14 @@
                 "render_shadows_in_compositor";
 
         /**
+         * Policy to be used for the display shade when connected to an external display.
+         * @hide
+         */
+        @Readable
+        public static final String DEVELOPMENT_SHADE_DISPLAY_AWARENESS =
+                "shade_display_awareness";
+
+        /**
          * Path to the WindowManager display settings file. If unset, the default file path will
          * be used.
          *
diff --git a/core/java/android/provider/flags.aconfig b/core/java/android/provider/flags.aconfig
index fff5363..a80be53 100644
--- a/core/java/android/provider/flags.aconfig
+++ b/core/java/android/provider/flags.aconfig
@@ -24,14 +24,6 @@
     bug: "290696572"
 }
 
-flag {
-    name: "backup_tasks_settings_screen"
-    is_exported: true
-    namespace: "backstage_power"
-    description: "Add a new settings page for the RUN_BACKUP_JOBS permission."
-    bug: "320563660"
-}
-
 # OWNER = tgunn TARGET=25Q1
 flag {
     name: "allow_config_maximum_call_log_entries_per_sim"
diff --git a/core/java/android/security/flags.aconfig b/core/java/android/security/flags.aconfig
index 34bae46..ebb6fb4 100644
--- a/core/java/android/security/flags.aconfig
+++ b/core/java/android/security/flags.aconfig
@@ -137,4 +137,12 @@
     description: "Feature flag for Secure Lockdown feature"
     bug: "373422357"
     is_exported: true
+}
+
+flag {
+    name: "subscribe_to_keyguard_locked_state_perm_priv_flag"
+    namespace: "psap_ai"
+    description: "Feature flag to add the privileged flag to the SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE permission"
+    bug: "380120712"
+    is_fixed_read_only: true
 }
\ No newline at end of file
diff --git a/core/java/android/service/autofill/augmented/FillWindow.java b/core/java/android/service/autofill/augmented/FillWindow.java
index 0ce040d..d42ec7c 100644
--- a/core/java/android/service/autofill/augmented/FillWindow.java
+++ b/core/java/android/service/autofill/augmented/FillWindow.java
@@ -17,6 +17,7 @@
 
 import static android.service.autofill.augmented.AugmentedAutofillService.sDebug;
 import static android.service.autofill.augmented.AugmentedAutofillService.sVerbose;
+import static android.service.autofill.Flags.addAccessibilityTitleForAugmentedAutofillDropdown;
 
 import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
 
@@ -36,6 +37,7 @@
 import android.view.autofill.IAutofillWindowPresenter;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.R;
 
 import dalvik.system.CloseGuard;
 
@@ -208,6 +210,12 @@
             if (mWm != null && mFillView != null) {
                 try {
                     p.flags |= WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
+                    if (addAccessibilityTitleForAugmentedAutofillDropdown()) {
+                        p.accessibilityTitle =
+                            mFillView
+                                    .getContext()
+                                    .getString(R.string.autofill_picker_accessibility_title);
+                    }
                     if (!mShowing) {
                         mWm.addView(mFillView, p);
                         mShowing = true;
diff --git a/core/java/android/service/notification/ZenDeviceEffects.java b/core/java/android/service/notification/ZenDeviceEffects.java
index 22b1be0..06bd255 100644
--- a/core/java/android/service/notification/ZenDeviceEffects.java
+++ b/core/java/android/service/notification/ZenDeviceEffects.java
@@ -42,21 +42,26 @@
 
     /**
      * Enum for the user-modifiable fields in this object.
+     *
      * @hide
      */
-    @IntDef(flag = true, prefix = { "FIELD_" }, value = {
-            FIELD_GRAYSCALE,
-            FIELD_SUPPRESS_AMBIENT_DISPLAY,
-            FIELD_DIM_WALLPAPER,
-            FIELD_NIGHT_MODE,
-            FIELD_DISABLE_AUTO_BRIGHTNESS,
-            FIELD_DISABLE_TAP_TO_WAKE,
-            FIELD_DISABLE_TILT_TO_WAKE,
-            FIELD_DISABLE_TOUCH,
-            FIELD_MINIMIZE_RADIO_USAGE,
-            FIELD_MAXIMIZE_DOZE,
-            FIELD_EXTRA_EFFECTS
-    })
+    @IntDef(
+            flag = true,
+            prefix = {"FIELD_"},
+            value = {
+                FIELD_GRAYSCALE,
+                FIELD_SUPPRESS_AMBIENT_DISPLAY,
+                FIELD_DIM_WALLPAPER,
+                FIELD_NIGHT_MODE,
+                FIELD_DISABLE_AUTO_BRIGHTNESS,
+                FIELD_DISABLE_TAP_TO_WAKE,
+                FIELD_DISABLE_TILT_TO_WAKE,
+                FIELD_DISABLE_TOUCH,
+                FIELD_MINIMIZE_RADIO_USAGE,
+                FIELD_MAXIMIZE_DOZE,
+                FIELD_NIGHT_LIGHT,
+                FIELD_EXTRA_EFFECTS
+            })
     @Retention(RetentionPolicy.SOURCE)
     public @interface ModifiableField {}
 
@@ -105,6 +110,9 @@
      */
     public static final int FIELD_EXTRA_EFFECTS = 1 << 10;
 
+    /** @hide */
+    public static final int FIELD_NIGHT_LIGHT = 1 << 11;
+
     private static final int MAX_EFFECTS_LENGTH = 2_000; // characters
 
     private final boolean mGrayscale;
@@ -118,12 +126,22 @@
     private final boolean mDisableTouch;
     private final boolean mMinimizeRadioUsage;
     private final boolean mMaximizeDoze;
+    private final boolean mNightLight;
     private final Set<String> mExtraEffects;
 
-    private ZenDeviceEffects(boolean grayscale, boolean suppressAmbientDisplay,
-            boolean dimWallpaper, boolean nightMode, boolean disableAutoBrightness,
-            boolean disableTapToWake, boolean disableTiltToWake, boolean disableTouch,
-            boolean minimizeRadioUsage, boolean maximizeDoze, Set<String> extraEffects) {
+    private ZenDeviceEffects(
+            boolean grayscale,
+            boolean suppressAmbientDisplay,
+            boolean dimWallpaper,
+            boolean nightMode,
+            boolean disableAutoBrightness,
+            boolean disableTapToWake,
+            boolean disableTiltToWake,
+            boolean disableTouch,
+            boolean minimizeRadioUsage,
+            boolean maximizeDoze,
+            boolean nightLight,
+            Set<String> extraEffects) {
         mGrayscale = grayscale;
         mSuppressAmbientDisplay = suppressAmbientDisplay;
         mDimWallpaper = dimWallpaper;
@@ -134,6 +152,7 @@
         mDisableTouch = disableTouch;
         mMinimizeRadioUsage = minimizeRadioUsage;
         mMaximizeDoze = maximizeDoze;
+        mNightLight = nightLight;
         mExtraEffects = Collections.unmodifiableSet(extraEffects);
     }
 
@@ -166,14 +185,25 @@
                 && this.mDisableTouch == that.mDisableTouch
                 && this.mMinimizeRadioUsage == that.mMinimizeRadioUsage
                 && this.mMaximizeDoze == that.mMaximizeDoze
+                && this.mNightLight == that.mNightLight
                 && Objects.equals(this.mExtraEffects, that.mExtraEffects);
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(mGrayscale, mSuppressAmbientDisplay, mDimWallpaper, mNightMode,
-                mDisableAutoBrightness, mDisableTapToWake, mDisableTiltToWake, mDisableTouch,
-                mMinimizeRadioUsage, mMaximizeDoze, mExtraEffects);
+        return Objects.hash(
+                mGrayscale,
+                mSuppressAmbientDisplay,
+                mDimWallpaper,
+                mNightMode,
+                mDisableAutoBrightness,
+                mDisableTapToWake,
+                mDisableTiltToWake,
+                mDisableTouch,
+                mMinimizeRadioUsage,
+                mMaximizeDoze,
+                mNightLight,
+                mExtraEffects);
     }
 
     @Override
@@ -189,6 +219,7 @@
         if (mDisableTouch) effects.add("disableTouch");
         if (mMinimizeRadioUsage) effects.add("minimizeRadioUsage");
         if (mMaximizeDoze) effects.add("maximizeDoze");
+        if (mNightLight) effects.add("nightLight");
         if (mExtraEffects.size() > 0) {
             effects.add("extraEffects=[" + String.join(",", mExtraEffects) + "]");
         }
@@ -228,6 +259,9 @@
         if ((bitmask & FIELD_MAXIMIZE_DOZE) != 0) {
             modified.add("FIELD_MAXIMIZE_DOZE");
         }
+        if (((bitmask) & FIELD_NIGHT_LIGHT) != 0) {
+            modified.add("FIELD_NIGHT_LIGHT");
+        }
         if ((bitmask & FIELD_EXTRA_EFFECTS) != 0) {
             modified.add("FIELD_EXTRA_EFFECTS");
         }
@@ -313,6 +347,15 @@
     }
 
     /**
+     * Whether the night display transformation should be activated while the rule is active.
+     *
+     * @hide
+     */
+    public boolean shouldUseNightLight() {
+        return mNightLight;
+    }
+
+    /**
      * (Immutable) set of extra effects to be applied while the rule is active. Extra effects are
      * not used in AOSP, but OEMs may add support for them by providing a custom
      * {@link DeviceEffectsApplier}.
@@ -329,29 +372,46 @@
      * @hide
      */
     public boolean hasEffects() {
-        return mGrayscale || mSuppressAmbientDisplay || mDimWallpaper || mNightMode
-                || mDisableAutoBrightness || mDisableTapToWake || mDisableTiltToWake
-                || mDisableTouch || mMinimizeRadioUsage || mMaximizeDoze
+        return mGrayscale
+                || mSuppressAmbientDisplay
+                || mDimWallpaper
+                || mNightMode
+                || mDisableAutoBrightness
+                || mDisableTapToWake
+                || mDisableTiltToWake
+                || mDisableTouch
+                || mMinimizeRadioUsage
+                || mMaximizeDoze
+                || mNightLight
                 || mExtraEffects.size() > 0;
     }
 
     /** {@link Parcelable.Creator} that instantiates {@link ZenDeviceEffects} objects. */
     @NonNull
-    public static final Creator<ZenDeviceEffects> CREATOR = new Creator<ZenDeviceEffects>() {
-        @Override
-        public ZenDeviceEffects createFromParcel(Parcel in) {
-            return new ZenDeviceEffects(in.readBoolean(),
-                    in.readBoolean(), in.readBoolean(), in.readBoolean(), in.readBoolean(),
-                    in.readBoolean(), in.readBoolean(), in.readBoolean(), in.readBoolean(),
-                    in.readBoolean(),
-                    Set.of(in.readArray(String.class.getClassLoader(), String.class)));
-        }
+    public static final Creator<ZenDeviceEffects> CREATOR =
+            new Creator<ZenDeviceEffects>() {
+                @Override
+                public ZenDeviceEffects createFromParcel(Parcel in) {
+                    return new ZenDeviceEffects(
+                            in.readBoolean(),
+                            in.readBoolean(),
+                            in.readBoolean(),
+                            in.readBoolean(),
+                            in.readBoolean(),
+                            in.readBoolean(),
+                            in.readBoolean(),
+                            in.readBoolean(),
+                            in.readBoolean(),
+                            in.readBoolean(),
+                            in.readBoolean(),
+                            Set.of(in.readArray(String.class.getClassLoader(), String.class)));
+                }
 
-        @Override
-        public ZenDeviceEffects[] newArray(int size) {
-            return new ZenDeviceEffects[size];
-        }
-    };
+                @Override
+                public ZenDeviceEffects[] newArray(int size) {
+                    return new ZenDeviceEffects[size];
+                }
+            };
 
     @Override
     public int describeContents() {
@@ -370,6 +430,7 @@
         dest.writeBoolean(mDisableTouch);
         dest.writeBoolean(mMinimizeRadioUsage);
         dest.writeBoolean(mMaximizeDoze);
+        dest.writeBoolean(mNightLight);
         dest.writeArray(mExtraEffects.toArray(new String[0]));
     }
 
@@ -387,6 +448,7 @@
         private boolean mDisableTouch;
         private boolean mMinimizeRadioUsage;
         private boolean mMaximizeDoze;
+        private boolean mNightLight;
         private final HashSet<String> mExtraEffects = new HashSet<>();
 
         /**
@@ -410,6 +472,7 @@
             mDisableTouch = zenDeviceEffects.shouldDisableTouch();
             mMinimizeRadioUsage = zenDeviceEffects.shouldMinimizeRadioUsage();
             mMaximizeDoze = zenDeviceEffects.shouldMaximizeDoze();
+            mNightLight = zenDeviceEffects.shouldUseNightLight();
             mExtraEffects.addAll(zenDeviceEffects.getExtraEffects());
         }
 
@@ -512,6 +575,18 @@
         }
 
         /**
+         * Sets whether the night display transformation should be activated while the rule is
+         * active.
+         *
+         * @hide
+         */
+        @NonNull
+        public Builder setShouldUseNightLight(boolean nightLight) {
+            mNightLight = nightLight;
+            return this;
+        }
+
+        /**
          * Sets the extra effects to be applied while the rule is active. Extra effects are not
          * used in AOSP, but OEMs may add support for them by providing a custom
          * {@link DeviceEffectsApplier}.
@@ -577,6 +652,7 @@
             if (effects.shouldDisableTouch()) setShouldDisableTouch(true);
             if (effects.shouldMinimizeRadioUsage()) setShouldMinimizeRadioUsage(true);
             if (effects.shouldMaximizeDoze()) setShouldMaximizeDoze(true);
+            if (effects.shouldUseNightLight()) setShouldUseNightLight(true);
             addExtraEffects(effects.getExtraEffects());
             return this;
         }
@@ -584,10 +660,19 @@
         /** Builds a {@link ZenDeviceEffects} object based on the builder's state. */
         @NonNull
         public ZenDeviceEffects build() {
-            return new ZenDeviceEffects(mGrayscale,
-                    mSuppressAmbientDisplay, mDimWallpaper, mNightMode, mDisableAutoBrightness,
-                    mDisableTapToWake, mDisableTiltToWake, mDisableTouch, mMinimizeRadioUsage,
-                    mMaximizeDoze, mExtraEffects);
+            return new ZenDeviceEffects(
+                    mGrayscale,
+                    mSuppressAmbientDisplay,
+                    mDimWallpaper,
+                    mNightMode,
+                    mDisableAutoBrightness,
+                    mDisableTapToWake,
+                    mDisableTiltToWake,
+                    mDisableTouch,
+                    mMinimizeRadioUsage,
+                    mMaximizeDoze,
+                    mNightLight,
+                    mExtraEffects);
         }
     }
 }
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index 1388778..4f459aa 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -325,6 +325,7 @@
     private static final String DEVICE_EFFECT_DISABLE_TOUCH = "zdeDisableTouch";
     private static final String DEVICE_EFFECT_MINIMIZE_RADIO_USAGE = "zdeMinimizeRadioUsage";
     private static final String DEVICE_EFFECT_MAXIMIZE_DOZE = "zdeMaximizeDoze";
+    private static final String DEVICE_EFFECT_USE_NIGHT_LIGHT = "zdeUseNightLight";
     private static final String DEVICE_EFFECT_EXTRAS = "zdeExtraEffects";
     private static final String DEVICE_EFFECT_USER_MODIFIED_FIELDS = "zdeUserModifiedFields";
 
@@ -1508,25 +1509,32 @@
     @FlaggedApi(Flags.FLAG_MODES_API)
     @Nullable
     private static ZenDeviceEffects readZenDeviceEffectsXml(TypedXmlPullParser parser) {
-        ZenDeviceEffects deviceEffects = new ZenDeviceEffects.Builder()
-                .setShouldDisplayGrayscale(
-                        safeBoolean(parser, DEVICE_EFFECT_DISPLAY_GRAYSCALE, false))
-                .setShouldSuppressAmbientDisplay(
-                        safeBoolean(parser, DEVICE_EFFECT_SUPPRESS_AMBIENT_DISPLAY, false))
-                .setShouldDimWallpaper(safeBoolean(parser, DEVICE_EFFECT_DIM_WALLPAPER, false))
-                .setShouldUseNightMode(safeBoolean(parser, DEVICE_EFFECT_USE_NIGHT_MODE, false))
-                .setShouldDisableAutoBrightness(
-                        safeBoolean(parser, DEVICE_EFFECT_DISABLE_AUTO_BRIGHTNESS, false))
-                .setShouldDisableTapToWake(
-                        safeBoolean(parser, DEVICE_EFFECT_DISABLE_TAP_TO_WAKE, false))
-                .setShouldDisableTiltToWake(
-                        safeBoolean(parser, DEVICE_EFFECT_DISABLE_TILT_TO_WAKE, false))
-                .setShouldDisableTouch(safeBoolean(parser, DEVICE_EFFECT_DISABLE_TOUCH, false))
-                .setShouldMinimizeRadioUsage(
-                        safeBoolean(parser, DEVICE_EFFECT_MINIMIZE_RADIO_USAGE, false))
-                .setShouldMaximizeDoze(safeBoolean(parser, DEVICE_EFFECT_MAXIMIZE_DOZE, false))
-                .setExtraEffects(safeStringSet(parser, DEVICE_EFFECT_EXTRAS))
-                .build();
+        ZenDeviceEffects deviceEffects =
+                new ZenDeviceEffects.Builder()
+                        .setShouldDisplayGrayscale(
+                                safeBoolean(parser, DEVICE_EFFECT_DISPLAY_GRAYSCALE, false))
+                        .setShouldSuppressAmbientDisplay(
+                                safeBoolean(parser, DEVICE_EFFECT_SUPPRESS_AMBIENT_DISPLAY, false))
+                        .setShouldDimWallpaper(
+                                safeBoolean(parser, DEVICE_EFFECT_DIM_WALLPAPER, false))
+                        .setShouldUseNightMode(
+                                safeBoolean(parser, DEVICE_EFFECT_USE_NIGHT_MODE, false))
+                        .setShouldDisableAutoBrightness(
+                                safeBoolean(parser, DEVICE_EFFECT_DISABLE_AUTO_BRIGHTNESS, false))
+                        .setShouldDisableTapToWake(
+                                safeBoolean(parser, DEVICE_EFFECT_DISABLE_TAP_TO_WAKE, false))
+                        .setShouldDisableTiltToWake(
+                                safeBoolean(parser, DEVICE_EFFECT_DISABLE_TILT_TO_WAKE, false))
+                        .setShouldDisableTouch(
+                                safeBoolean(parser, DEVICE_EFFECT_DISABLE_TOUCH, false))
+                        .setShouldMinimizeRadioUsage(
+                                safeBoolean(parser, DEVICE_EFFECT_MINIMIZE_RADIO_USAGE, false))
+                        .setShouldMaximizeDoze(
+                                safeBoolean(parser, DEVICE_EFFECT_MAXIMIZE_DOZE, false))
+                        .setShouldUseNightLight(
+                                safeBoolean(parser, DEVICE_EFFECT_USE_NIGHT_LIGHT, false))
+                        .setExtraEffects(safeStringSet(parser, DEVICE_EFFECT_EXTRAS))
+                        .build();
 
         return deviceEffects.hasEffects() ? deviceEffects : null;
     }
@@ -1550,6 +1558,7 @@
         writeBooleanIfTrue(out, DEVICE_EFFECT_MINIMIZE_RADIO_USAGE,
                 deviceEffects.shouldMinimizeRadioUsage());
         writeBooleanIfTrue(out, DEVICE_EFFECT_MAXIMIZE_DOZE, deviceEffects.shouldMaximizeDoze());
+        writeBooleanIfTrue(out, DEVICE_EFFECT_USE_NIGHT_LIGHT, deviceEffects.shouldUseNightLight());
         writeStringSet(out, DEVICE_EFFECT_EXTRAS, deviceEffects.getExtraEffects());
     }
 
diff --git a/core/java/android/service/notification/ZenModeDiff.java b/core/java/android/service/notification/ZenModeDiff.java
index c9f4647..31acd24 100644
--- a/core/java/android/service/notification/ZenModeDiff.java
+++ b/core/java/android/service/notification/ZenModeDiff.java
@@ -714,6 +714,7 @@
         public static final String FIELD_DISABLE_TOUCH = "mDisableTouch";
         public static final String FIELD_MINIMIZE_RADIO_USAGE = "mMinimizeRadioUsage";
         public static final String FIELD_MAXIMIZE_DOZE = "mMaximizeDoze";
+        public static final String FIELD_NIGHT_LIGHT = "mNightLight";
         public static final String FIELD_EXTRA_EFFECTS = "mExtraEffects";
         // NOTE: new field strings must match the variable names in ZenDeviceEffects
 
@@ -781,6 +782,11 @@
                 addField(FIELD_MAXIMIZE_DOZE, new FieldDiff<>(from.shouldMaximizeDoze(),
                         to.shouldMaximizeDoze()));
             }
+            if (from.shouldUseNightLight() != to.shouldUseNightLight()) {
+                addField(
+                        FIELD_NIGHT_LIGHT,
+                        new FieldDiff<>(from.shouldUseNightLight(), to.shouldUseNightLight()));
+            }
             if (!Objects.equals(from.getExtraEffects(), to.getExtraEffects())) {
                 addField(FIELD_EXTRA_EFFECTS, new FieldDiff<>(from.getExtraEffects(),
                         to.getExtraEffects()));
diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java
index cb72b97..6dc82c4 100644
--- a/core/java/android/text/TextUtils.java
+++ b/core/java/android/text/TextUtils.java
@@ -678,7 +678,7 @@
      * @return true if a and b are equal
      */
     @android.ravenwood.annotation.RavenwoodKeep
-    public static boolean equals(CharSequence a, CharSequence b) {
+    public static boolean equals(@Nullable CharSequence a, @Nullable CharSequence b) {
         if (a == b) return true;
         int length;
         if (a != null && b != null && (length = a.length()) == b.length()) {
diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java
index 8cb96ae..089b5c2 100644
--- a/core/java/android/view/Choreographer.java
+++ b/core/java/android/view/Choreographer.java
@@ -42,6 +42,7 @@
 
 import java.io.PrintWriter;
 import java.util.Locale;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
  * Coordinates the timing of animations, input and drawing.
@@ -208,7 +209,7 @@
     private final FrameData mFrameData = new FrameData();
     private volatile boolean mInDoFrameCallback = false;
 
-    private static class BufferStuffingData {
+    private static class BufferStuffingState {
         enum RecoveryAction {
             // No recovery
             NONE,
@@ -218,21 +219,15 @@
             // back toward threshold.
             DELAY_FRAME
         }
-        // The maximum number of times frames will be delayed per buffer stuffing event.
-        // Since buffer stuffing can persist for several consecutive frames following the
-        // initial missed frame, we want to adjust the timeline with enough frame delays and
-        // offsets to return the queued buffer count back to threshold.
-        public static final int MAX_FRAME_DELAYS = 3;
+        // Indicates if recovery should begin. Is true whenever the client was blocked
+        // on dequeuing a buffer. When buffer stuffing recovery begins, this is reset
+        // since the scheduled frame delay reduces the number of queued buffers.
+        public AtomicBoolean isStuffed = new AtomicBoolean(false);
 
         // Whether buffer stuffing recovery has begun. Recovery can only end
         // when events are idle.
         public boolean isRecovering = false;
 
-        // The number of frames delayed so far during recovery. Used to compare with
-        // MAX_FRAME_DELAYS to safeguard against excessive frame delays during recovery.
-        // Also used as unique cookie for tracing.
-        public int numberFrameDelays = 0;
-
         // The number of additional frame delays scheduled during recovery to wait for the next
         // vsync. These are scheduled when frame times appear to go backward or frames are
         // being skipped due to FPSDivisor.
@@ -245,12 +240,20 @@
          */
         public void reset() {
             isRecovering = false;
-            numberFrameDelays = 0;
             numberWaitsForNextVsync = 0;
         }
     }
 
-    private final BufferStuffingData mBufferStuffingData = new BufferStuffingData();
+    private final BufferStuffingState mBufferStuffingState = new BufferStuffingState();
+
+    /**
+     * Set flag to indicate that client is blocked waiting for buffer release and
+     * buffer stuffing recovery should soon begin.
+     * @hide
+     */
+    public void onWaitForBufferRelease() {
+        mBufferStuffingState.isStuffed.set(true);
+    }
 
     /**
      * Contains information about the current frame for jank-tracking,
@@ -901,67 +904,56 @@
 
     // Conducts logic for beginning or ending buffer stuffing recovery.
     // Returns an enum for the recovery action that should be taken in doFrame().
-    BufferStuffingData.RecoveryAction checkBufferStuffingRecovery(long frameTimeNanos,
+    BufferStuffingState.RecoveryAction updateBufferStuffingState(long frameTimeNanos,
             DisplayEventReceiver.VsyncEventData vsyncEventData) {
-        // Canned animations can recover from buffer stuffing whenever more
-        // than 2 buffers are queued.
-        if (vsyncEventData.numberQueuedBuffers > 2) {
-            mBufferStuffingData.isRecovering = true;
-            // Intentional frame delay that can happen at most MAX_FRAME_DELAYS times per
-            // buffer stuffing event until the buffer count returns to threshold. The
-            // delayed frames are compensated for by the negative offsets added to the
-            // animation timestamps.
-            if (mBufferStuffingData.numberFrameDelays < mBufferStuffingData.MAX_FRAME_DELAYS) {
-                if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
-                    Trace.asyncTraceForTrackBegin(
-                            Trace.TRACE_TAG_VIEW, "Buffer stuffing recovery", "Thread "
-                            + android.os.Process.myTid() + ", recover frame #"
-                            + mBufferStuffingData.numberFrameDelays,
-                            mBufferStuffingData.numberFrameDelays);
-                }
-                mBufferStuffingData.numberFrameDelays++;
-                scheduleVsyncLocked();
-                return BufferStuffingData.RecoveryAction.DELAY_FRAME;
+        if (!mBufferStuffingState.isRecovering) {
+            if (!mBufferStuffingState.isStuffed.getAndSet(false)) {
+                return BufferStuffingState.RecoveryAction.NONE;
             }
+            // Canned animations can recover from buffer stuffing whenever the
+            // client is blocked on dequeueBuffer. Frame delay only occurs at
+            // the start of recovery to free a buffer.
+            mBufferStuffingState.isRecovering = true;
+            if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
+                Trace.asyncTraceForTrackBegin(
+                        Trace.TRACE_TAG_VIEW, "Buffer stuffing recovery", "Thread "
+                        + android.os.Process.myTid() + ", recover frame", 0);
+            }
+            return BufferStuffingState.RecoveryAction.DELAY_FRAME;
         }
 
-        if (mBufferStuffingData.isRecovering) {
-            // Includes an additional expected frame delay from the natural scheduling
-            // of the next vsync event.
-            int totalFrameDelays = mBufferStuffingData.numberFrameDelays
-                    + mBufferStuffingData.numberWaitsForNextVsync + 1;
-            long vsyncsSinceLastCallback = mLastFrameIntervalNanos > 0
-                    ? (frameTimeNanos - mLastNoOffsetFrameTimeNanos) / mLastFrameIntervalNanos : 0;
+        // Total number of frame delays used to detect idle state. Includes an additional
+        // expected frame delay from the natural scheduling of the next vsync event and
+        // the intentional frame delay that was scheduled when stuffing was first detected.
+        int totalFrameDelays = mBufferStuffingState.numberWaitsForNextVsync + 2;
+        long vsyncsSinceLastCallback = mLastFrameIntervalNanos > 0
+                ? (frameTimeNanos - mLastNoOffsetFrameTimeNanos) / mLastFrameIntervalNanos : 0;
 
-            // Detected idle state due to a longer inactive period since the last vsync callback
-            // than the total expected number of vsync frame delays. End buffer stuffing recovery.
-            // There are no frames to animate and offsets no longer need to be added
-            // since the idle state gives the animation a chance to catch up.
-            if (vsyncsSinceLastCallback > totalFrameDelays) {
-                if (DEBUG_JANK) {
-                    Log.d(TAG, "End buffer stuffing recovery");
-                }
-                if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
-                    for (int i = 0; i < mBufferStuffingData.numberFrameDelays; i++) {
-                        Trace.asyncTraceForTrackEnd(
-                                Trace.TRACE_TAG_VIEW, "Buffer stuffing recovery", i);
-                    }
-                }
-                mBufferStuffingData.reset();
-
-            } else {
-                if (DEBUG_JANK) {
-                    Log.d(TAG, "Adjust animation timeline with a negative offset");
-                }
-                if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
-                    Trace.instantForTrack(
-                            Trace.TRACE_TAG_VIEW, "Buffer stuffing recovery",
-                            "Negative offset added to animation");
-                }
-                return BufferStuffingData.RecoveryAction.OFFSET;
+        // Detected idle state due to a longer inactive period since the last vsync callback
+        // than the total expected number of vsync frame delays. End buffer stuffing recovery.
+        // There are no frames to animate and offsets no longer need to be added
+        // since the idle state gives the animation a chance to catch up.
+        if (vsyncsSinceLastCallback > totalFrameDelays) {
+            if (DEBUG_JANK) {
+                Log.d(TAG, "End buffer stuffing recovery");
             }
+            if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
+                Trace.asyncTraceForTrackEnd(
+                        Trace.TRACE_TAG_VIEW, "Buffer stuffing recovery", 0);
+            }
+            mBufferStuffingState.reset();
+            return BufferStuffingState.RecoveryAction.NONE;
         }
-        return BufferStuffingData.RecoveryAction.NONE;
+
+        if (DEBUG_JANK) {
+            Log.d(TAG, "Adjust animation timeline with a negative offset");
+        }
+        if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
+            Trace.instantForTrack(
+                    Trace.TRACE_TAG_VIEW, "Buffer stuffing recovery",
+                    "Negative offset added to animation");
+        }
+        return BufferStuffingState.RecoveryAction.OFFSET;
     }
 
     void doFrame(long frameTimeNanos, int frame,
@@ -973,7 +965,7 @@
 
         // Evaluate if buffer stuffing recovery needs to start or end, and
         // what actions need to be taken for recovery.
-        switch (checkBufferStuffingRecovery(frameTimeNanos, vsyncEventData)) {
+        switch (updateBufferStuffingState(frameTimeNanos, vsyncEventData)) {
             case NONE:
                 // Without buffer stuffing recovery, offsetFrameTimeNanos is
                 // synonymous with frameTimeNanos.
@@ -984,7 +976,8 @@
                 offsetFrameTimeNanos = frameTimeNanos - frameIntervalNanos;
                 break;
             case DELAY_FRAME:
-                // Intentional frame delay to help restore queued buffer count to threshold.
+                // Intentional frame delay to help reduce queued buffer count.
+                scheduleVsyncLocked();
                 return;
             default:
                 break;
@@ -1037,7 +1030,7 @@
                                     + " ms in the past.");
                         }
                     }
-                    if (mBufferStuffingData.isRecovering) {
+                    if (mBufferStuffingState.isRecovering) {
                         frameTimeNanos -= frameIntervalNanos;
                         if (DEBUG_JANK) {
                             Log.d(TAG, "Adjusted animation timeline with a negative offset after"
@@ -1055,8 +1048,8 @@
                                 + "previously skipped frame.  Waiting for next vsync.");
                     }
                     traceMessage("Frame time goes backward");
-                    if (mBufferStuffingData.isRecovering) {
-                        mBufferStuffingData.numberWaitsForNextVsync++;
+                    if (mBufferStuffingState.isRecovering) {
+                        mBufferStuffingState.numberWaitsForNextVsync++;
                     }
                     scheduleVsyncLocked();
                     return;
@@ -1066,8 +1059,8 @@
                     long timeSinceVsync = frameTimeNanos - mLastFrameTimeNanos;
                     if (timeSinceVsync < (frameIntervalNanos * mFPSDivisor) && timeSinceVsync > 0) {
                         traceMessage("Frame skipped due to FPSDivisor");
-                        if (mBufferStuffingData.isRecovering) {
-                            mBufferStuffingData.numberWaitsForNextVsync++;
+                        if (mBufferStuffingState.isRecovering) {
+                            mBufferStuffingState.numberWaitsForNextVsync++;
                         }
                         scheduleVsyncLocked();
                         return;
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 802bddd..0c8a0d6 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -19,6 +19,7 @@
 import static android.Manifest.permission.CONFIGURE_DISPLAY_COLOR_MODE;
 import static android.Manifest.permission.CONTROL_DISPLAY_BRIGHTNESS;
 import static android.hardware.flags.Flags.FLAG_OVERLAYPROPERTIES_CLASS_API;
+import static android.util.TypedValue.COMPLEX_UNIT_DIP;
 
 import static com.android.server.display.feature.flags.Flags.FLAG_ENABLE_GET_SUPPORTED_REFRESH_RATES;
 import static com.android.server.display.feature.flags.Flags.FLAG_HIGHEST_HDR_SDR_RATIO_API;
@@ -58,6 +59,7 @@
 import android.util.ArraySet;
 import android.util.DisplayMetrics;
 import android.util.Log;
+import android.util.TypedValue;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -1048,6 +1050,19 @@
     }
 
     /**
+     * Returns the smallest size of the display in dp
+     * @hide
+     */
+    public float getMinSizeDimensionDp() {
+        synchronized (mLock) {
+            updateDisplayInfoLocked();
+            mDisplayInfo.getAppMetrics(mTempMetrics);
+            return TypedValue.deriveDimension(COMPLEX_UNIT_DIP,
+                    Math.min(mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight), mTempMetrics);
+        }
+    }
+
+    /**
      * @deprecated Use {@link WindowMetrics#getBounds#width()} instead.
      */
     @Deprecated
@@ -2041,6 +2056,22 @@
     }
 
     /**
+     * Returns whether the display is eligible for hosting tasks.
+     *
+     * For example, if the display is used for mirroring, this will return {@code false}.
+     *
+     * TODO (b/383666349): Rename this later once there is a better option.
+     *
+     * @hide
+     */
+    public boolean canHostTasks() {
+        synchronized (mLock) {
+            updateDisplayInfoLocked();
+            return mIsValid && mDisplayInfo.canHostTasks;
+        }
+    }
+
+    /**
      * Returns true if the specified UID has access to this display.
      * @hide
      */
diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java
index 4307884..ba098eb5 100644
--- a/core/java/android/view/DisplayInfo.java
+++ b/core/java/android/view/DisplayInfo.java
@@ -408,6 +408,15 @@
     @Nullable
     public String thermalBrightnessThrottlingDataId;
 
+    /**
+     * Indicates whether the display is eligible for hosting tasks.
+     *
+     * For example, if the display is used for mirroring, this will be {@code false}.
+     *
+     * @hide
+     */
+    public boolean canHostTasks;
+
     public static final @android.annotation.NonNull Creator<DisplayInfo> CREATOR = new Creator<DisplayInfo>() {
         @Override
         public DisplayInfo createFromParcel(Parcel source) {
@@ -493,7 +502,8 @@
                 && BrightnessSynchronizer.floatEquals(hdrSdrRatio, other.hdrSdrRatio)
                 && thermalRefreshRateThrottling.contentEquals(other.thermalRefreshRateThrottling)
                 && Objects.equals(
-                thermalBrightnessThrottlingDataId, other.thermalBrightnessThrottlingDataId);
+                thermalBrightnessThrottlingDataId, other.thermalBrightnessThrottlingDataId)
+                && canHostTasks == other.canHostTasks;
     }
 
     @Override
@@ -561,6 +571,7 @@
         hdrSdrRatio = other.hdrSdrRatio;
         thermalRefreshRateThrottling = other.thermalRefreshRateThrottling;
         thermalBrightnessThrottlingDataId = other.thermalBrightnessThrottlingDataId;
+        canHostTasks = other.canHostTasks;
     }
 
     public void readFromParcel(Parcel source) {
@@ -642,6 +653,7 @@
         thermalRefreshRateThrottling = source.readSparseArray(null,
                 SurfaceControl.RefreshRateRange.class);
         thermalBrightnessThrottlingDataId = source.readString8();
+        canHostTasks = source.readBoolean();
     }
 
     @Override
@@ -717,6 +729,7 @@
         dest.writeFloat(hdrSdrRatio);
         dest.writeSparseArray(thermalRefreshRateThrottling);
         dest.writeString8(thermalBrightnessThrottlingDataId);
+        dest.writeBoolean(canHostTasks);
     }
 
     @Override
@@ -1020,6 +1033,8 @@
         sb.append(thermalRefreshRateThrottling);
         sb.append(", thermalBrightnessThrottlingDataId ");
         sb.append(thermalBrightnessThrottlingDataId);
+        sb.append(", canHostTasks ");
+        sb.append(canHostTasks);
         sb.append("}");
         return sb.toString();
     }
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index dd9a95e..f22505b 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -4671,8 +4671,7 @@
          * Sets the importance the layer's contents has to the app's user experience.
          * <p>
          * When a two layers within the same app are competing for a limited rendering resource,
-         * the priority will determine which layer gets access to the resource. The lower the
-         * priority, the more likely the layer will get access to the resource.
+         * the layer with the highest priority will gets access to the resource.
          * <p>
          * Resources managed by this priority:
          * <ul>
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index c1b92ee3..1d27574 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -2772,6 +2772,7 @@
         mBlastBufferQueue = new BLASTBufferQueue(mTag, mSurfaceControl,
                 mSurfaceSize.x, mSurfaceSize.y, mWindowAttributes.format);
         mBlastBufferQueue.setTransactionHangCallback(sTransactionHangCallback);
+        mBlastBufferQueue.setWaitForBufferReleaseCallback(mChoreographer::onWaitForBufferRelease);
         // If we create and destroy BBQ without recreating the SurfaceControl, we can end up
         // queuing buffers on multiple apply tokens causing out of order buffer submissions. We
         // fix this by setting the same apply token on all BBQs created by this VRI.
@@ -4434,7 +4435,8 @@
                 // merged with a sync group or BLASTBufferQueue before making it to this point
                 // But better a one or two frame flicker than steady-state broken from dropping
                 // whatever is in this transaction
-                mPendingTransaction.apply();
+                // apply immediately with bbq apply token
+                mergeWithNextTransaction(mPendingTransaction, 0);
                 mHasPendingTransactions = false;
             }
             mSyncBuffer = false;
@@ -5500,7 +5502,8 @@
                 Log.d(mTag, "Pending transaction will not be applied in sync with a draw due to "
                         + logReason);
             }
-            pendingTransaction.apply();
+            // apply immediately with bbq apply token
+            mergeWithNextTransaction(pendingTransaction, 0);
         }
     }
     /**
diff --git a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
index 049ad20..294e5da 100644
--- a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
+++ b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
@@ -233,6 +233,16 @@
 }
 
 flag {
+    name: "restore_a11y_secure_settings_on_hsum_device"
+    namespace: "accessibility"
+    description: "Grab the a11y settings and send the settings restored broadcast for current visible foreground user"
+    bug: "381294327"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
+
+flag {
     name: "supplemental_description"
     namespace: "accessibility"
     description: "Feature flag for supplemental description api"
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index ab5969b..14b208a 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -1788,4 +1788,17 @@
      * @see #setDisabledActionModeMenuItems
      */
     public static final int MENU_ITEM_PROCESS_TEXT = 1 << 2;
+
+    /**
+     * Enable CHIPS for webview.
+     * This provides a means to check if partitioned cookies are enabled by default.
+     * CHIPS will only be enabled by default for apps targeting Android B or above.
+     *
+     * @hide
+     */
+    @ChangeId
+    @EnabledAfter(targetSdkVersion = android.os.Build.VERSION_CODES.VANILLA_ICE_CREAM)
+    @FlaggedApi(android.webkit.Flags.FLAG_ENABLE_CHIPS)
+    @SystemApi
+    public static final long ENABLE_CHIPS = 380890146L;
 }
diff --git a/core/java/android/webkit/flags.aconfig b/core/java/android/webkit/flags.aconfig
index c5176a2..16cbb8a 100644
--- a/core/java/android/webkit/flags.aconfig
+++ b/core/java/android/webkit/flags.aconfig
@@ -36,6 +36,14 @@
 }
 
 flag {
+    name: "enable_chips"
+    is_exported: true
+    namespace: "webview"
+    description: "New feature enable CHIPS for webview"
+    bug: "359448044"
+}
+
+flag {
     name: "file_system_access"
     is_exported: true
     namespace: "webview"
diff --git a/core/java/android/window/BackProgressAnimator.java b/core/java/android/window/BackProgressAnimator.java
index b535eff..f61eb30d 100644
--- a/core/java/android/window/BackProgressAnimator.java
+++ b/core/java/android/window/BackProgressAnimator.java
@@ -52,6 +52,7 @@
      */
     private static final float SCALE_FACTOR = 100f;
     private static final float FLING_FRICTION = 8f;
+    private static final float BUTTON_SPRING_STIFFNESS = 100;
     private final SpringAnimation mSpring;
     private ProgressCallback mCallback;
     private float mProgress = 0;
@@ -156,7 +157,7 @@
                 /* frameTime */ System.nanoTime() / TimeUtils.NANOS_PER_MS);
         if (predictiveBackSwipeEdgeNoneApi()) {
             if (event.getSwipeEdge() == EDGE_NONE) {
-                mButtonSpringForce.setStiffness(SpringForce.STIFFNESS_LOW);
+                mButtonSpringForce.setStiffness(BUTTON_SPRING_STIFFNESS);
                 mSpring.setSpring(mButtonSpringForce);
                 mSpring.animateToFinalPosition(SCALE_FACTOR);
             } else {
diff --git a/core/java/android/window/DesktopModeFlags.java b/core/java/android/window/DesktopModeFlags.java
index 289c5cf..be69d3d 100644
--- a/core/java/android/window/DesktopModeFlags.java
+++ b/core/java/android/window/DesktopModeFlags.java
@@ -86,7 +86,9 @@
     ENABLE_DESKTOP_APP_LAUNCH_ALTTAB_TRANSITIONS_BUGFIX(
             Flags::enableDesktopAppLaunchAlttabTransitionsBugfix, false),
     ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS_BUGFIX(
-            Flags::enableDesktopAppLaunchTransitionsBugfix, false);
+            Flags::enableDesktopAppLaunchTransitionsBugfix, false),
+    INCLUDE_TOP_TRANSPARENT_FULLSCREEN_TASK_IN_DESKTOP_HEURISTIC(
+            Flags::includeTopTransparentFullscreenTaskInDesktopHeuristic, true);
 
     private static final String TAG = "DesktopModeFlagsUtil";
     // Function called to obtain aconfig flag value.
diff --git a/core/java/android/window/ScreenCapture.java b/core/java/android/window/ScreenCapture.java
index 5446428..fc41307 100644
--- a/core/java/android/window/ScreenCapture.java
+++ b/core/java/android/window/ScreenCapture.java
@@ -20,6 +20,7 @@
 import android.annotation.Nullable;
 import android.graphics.Bitmap;
 import android.graphics.ColorSpace;
+import android.graphics.Gainmap;
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
 import android.hardware.HardwareBuffer;
@@ -184,17 +185,29 @@
      * @hide
      */
     public static class ScreenshotHardwareBuffer {
+        private static final float EPSILON = 1.0f / 64.0f;
+
         private final HardwareBuffer mHardwareBuffer;
         private final ColorSpace mColorSpace;
         private final boolean mContainsSecureLayers;
         private final boolean mContainsHdrLayers;
+        private final HardwareBuffer mGainmap;
+        private final float mHdrSdrRatio;
 
         public ScreenshotHardwareBuffer(HardwareBuffer hardwareBuffer, ColorSpace colorSpace,
                 boolean containsSecureLayers, boolean containsHdrLayers) {
+            this(hardwareBuffer, colorSpace, containsSecureLayers, containsHdrLayers, null, 1.0f);
+        }
+
+        public ScreenshotHardwareBuffer(HardwareBuffer hardwareBuffer, ColorSpace colorSpace,
+                boolean containsSecureLayers, boolean containsHdrLayers, HardwareBuffer gainmap,
+                float hdrSdrRatio) {
             mHardwareBuffer = hardwareBuffer;
             mColorSpace = colorSpace;
             mContainsSecureLayers = containsSecureLayers;
             mContainsHdrLayers = containsHdrLayers;
+            mGainmap = gainmap;
+            mHdrSdrRatio = hdrSdrRatio;
         }
 
         /**
@@ -209,13 +222,12 @@
          * @param containsHdrLayers    Indicates whether this graphic buffer contains HDR content.
          */
         private static ScreenshotHardwareBuffer createFromNative(HardwareBuffer hardwareBuffer,
-                int dataspace, boolean containsSecureLayers, boolean containsHdrLayers) {
+                int dataspace, boolean containsSecureLayers, boolean containsHdrLayers,
+                HardwareBuffer gainmap, float hdrSdrRatio) {
             ColorSpace colorSpace = ColorSpace.getFromDataSpace(dataspace);
-            return new ScreenshotHardwareBuffer(
-                    hardwareBuffer,
+            return new ScreenshotHardwareBuffer(hardwareBuffer,
                     colorSpace != null ? colorSpace : ColorSpace.get(ColorSpace.Named.SRGB),
-                    containsSecureLayers,
-                    containsHdrLayers);
+                    containsSecureLayers, containsHdrLayers, gainmap, hdrSdrRatio);
         }
 
         public ColorSpace getColorSpace() {
@@ -259,7 +271,22 @@
                 Log.w(TAG, "Failed to take screenshot. Null screenshot object");
                 return null;
             }
-            return Bitmap.wrapHardwareBuffer(mHardwareBuffer, mColorSpace);
+
+            Bitmap bitmap = Bitmap.wrapHardwareBuffer(mHardwareBuffer, mColorSpace);
+            if (mGainmap != null) {
+                Bitmap gainmapBitmap = Bitmap.wrapHardwareBuffer(mGainmap, null);
+                Gainmap gainmap = new Gainmap(gainmapBitmap);
+                gainmap.setRatioMin(1.0f, 1.0f, 1.0f);
+                gainmap.setRatioMax(mHdrSdrRatio, mHdrSdrRatio, mHdrSdrRatio);
+                gainmap.setGamma(1.0f, 1.0f, 1.0f);
+                gainmap.setEpsilonSdr(EPSILON, EPSILON, EPSILON);
+                gainmap.setEpsilonHdr(EPSILON, EPSILON, EPSILON);
+                gainmap.setMinDisplayRatioForHdrTransition(1.0f);
+                gainmap.setDisplayRatioForFullHdr(mHdrSdrRatio);
+                bitmap.setGainmap(gainmap);
+            }
+
+            return bitmap;
         }
     }
 
diff --git a/core/java/android/window/TaskSnapshot.java b/core/java/android/window/TaskSnapshot.java
index a37bef8..53c64bd 100644
--- a/core/java/android/window/TaskSnapshot.java
+++ b/core/java/android/window/TaskSnapshot.java
@@ -77,6 +77,8 @@
     private final ColorSpace mColorSpace;
     private int mInternalReferences;
 
+    /** Keep in cache, doesn't need reference. */
+    public static final int REFERENCE_NONE = 0;
     /** This snapshot object is being broadcast. */
     public static final int REFERENCE_BROADCAST = 1;
     /** This snapshot object is in the cache. */
@@ -85,11 +87,16 @@
     public static final int REFERENCE_PERSIST = 1 << 2;
     /** This snapshot object is being used for content suggestion. */
     public static final int REFERENCE_CONTENT_SUGGESTION = 1 << 3;
+    /** This snapshot object will be passing to external process. Keep the snapshot reference after
+     * writeToParcel*/
+    public static final int REFERENCE_WRITE_TO_PARCEL = 1 << 4;
     @IntDef(flag = true, prefix = { "REFERENCE_" }, value = {
+            REFERENCE_NONE,
             REFERENCE_BROADCAST,
             REFERENCE_CACHE,
             REFERENCE_PERSIST,
-            REFERENCE_CONTENT_SUGGESTION
+            REFERENCE_CONTENT_SUGGESTION,
+            REFERENCE_WRITE_TO_PARCEL
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface ReferenceFlags {}
@@ -309,6 +316,11 @@
         dest.writeBoolean(mIsTranslucent);
         dest.writeBoolean(mHasImeSurface);
         dest.writeInt(mUiMode);
+        synchronized (this) {
+            if ((mInternalReferences & REFERENCE_WRITE_TO_PARCEL) != 0) {
+                removeReference(REFERENCE_WRITE_TO_PARCEL);
+            }
+        }
     }
 
     @Override
diff --git a/core/java/android/window/flags/lse_desktop_experience.aconfig b/core/java/android/window/flags/lse_desktop_experience.aconfig
index cfe44f8..3b77b1f 100644
--- a/core/java/android/window/flags/lse_desktop_experience.aconfig
+++ b/core/java/android/window/flags/lse_desktop_experience.aconfig
@@ -16,6 +16,17 @@
 }
 
 flag {
+    name: "include_top_transparent_fullscreen_task_in_desktop_heuristic"
+    namespace: "lse_desktop_experience"
+    description: "Whether to include any top transparent fullscreen task launched in desktop /n"
+                 "mode in the heuristic for if desktop windowing is showing or not."
+    bug: "379543275"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
+
+flag {
     name: "enable_windowing_dynamic_initial_bounds"
     namespace: "lse_desktop_experience"
     description: "Enables new initial bounds for desktop windowing which adjust depending on app constraints"
@@ -175,13 +186,6 @@
 }
 
 flag {
-    name: "enable_a11y_metrics"
-    namespace: "lse_desktop_experience"
-    description: "Whether to enable log collection for a11y actions in desktop windowing mode"
-    bug: "341319597"
-}
-
-flag {
     name: "enable_caption_compat_inset_force_consumption"
     namespace: "lse_desktop_experience"
     description: "Enables force-consumption of caption bar insets for immersive apps in freeform"
@@ -510,4 +514,11 @@
     metadata {
         purpose: PURPOSE_BUGFIX
     }
+}
+
+flag {
+    name: "enable_per_display_desktop_wallpaper_activity"
+    namespace: "lse_desktop_experience"
+    description: "Enables having a DesktopWallpaperActivity at a per-display level."
+    bug: "381935663"
 }
\ No newline at end of file
diff --git a/core/java/android/window/flags/windowing_frontend.aconfig b/core/java/android/window/flags/windowing_frontend.aconfig
index 1c27515..30668a6 100644
--- a/core/java/android/window/flags/windowing_frontend.aconfig
+++ b/core/java/android/window/flags/windowing_frontend.aconfig
@@ -9,16 +9,6 @@
 }
 
 flag {
-  name: "reset_draw_state_on_client_invisible"
-  namespace: "windowing_frontend"
-  description: "Reset draw state if the client is notified to be invisible"
-  bug: "373023636"
-  metadata {
-    purpose: PURPOSE_BUGFIX
-  }
-}
-
-flag {
     name: "wait_for_transition_on_display_switch"
     namespace: "windowing_frontend"
     description: "Waits for Shell transition to start before unblocking the screen after display switch"
@@ -39,16 +29,6 @@
 }
 
 flag {
-    name: "blast_sync_notification_shade_on_display_switch"
-    namespace: "windowing_frontend"
-    description: "Make the buffer content of notification shade synchronize with display switch"
-    bug: "337154331"
-    metadata {
-        purpose: PURPOSE_BUGFIX
-    }
-}
-
-flag {
   name: "respect_animation_clip"
   namespace: "windowing_frontend"
   description: "Fix missing clip transformation of animation"
@@ -207,16 +187,6 @@
 }
 
 flag {
-  name: "filter_irrelevant_input_device_change"
-  namespace: "windowing_frontend"
-  description: "Recompute display configuration only for necessary input device changes"
-  bug: "368461853"
-  metadata {
-    purpose: PURPOSE_BUGFIX
-  }
-}
-
-flag {
   name: "respect_non_top_visible_fixed_orientation"
   namespace: "windowing_frontend"
   description: "If top activity is not opaque, respect the fixed orientation of activity behind it"
diff --git a/core/java/android/window/flags/windowing_sdk.aconfig b/core/java/android/window/flags/windowing_sdk.aconfig
index d0d4af6..5a092b8 100644
--- a/core/java/android/window/flags/windowing_sdk.aconfig
+++ b/core/java/android/window/flags/windowing_sdk.aconfig
@@ -127,3 +127,14 @@
         purpose: PURPOSE_BUGFIX
     }
 }
+
+flag {
+    namespace: "windowing_sdk"
+    name: "track_system_ui_context_before_wms"
+    description: "Keep track of SystemUiContext before WMS is initialized"
+    bug: "384428048"
+    is_fixed_read_only: true
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
diff --git a/core/java/com/android/internal/app/NfcResolverActivity.java b/core/java/com/android/internal/app/NfcResolverActivity.java
index 78427fe..f15dbd6 100644
--- a/core/java/com/android/internal/app/NfcResolverActivity.java
+++ b/core/java/com/android/internal/app/NfcResolverActivity.java
@@ -34,13 +34,13 @@
     @Override
     @SuppressWarnings("MissingSuperCall")  // Called indirectly via `super_onCreate()`.
     protected void onCreate(Bundle savedInstanceState) {
-        if (!enableNfcMainline()) {
+        Intent intent = getIntent();
+        if (!enableNfcMainline() || intent.getExtras() == null) {
             super_onCreate(savedInstanceState);
             finish();
             return;
         }
 
-        Intent intent = getIntent();
         Intent target = intent.getParcelableExtra(Intent.EXTRA_INTENT, Intent.class);
         ArrayList<ResolveInfo> rList =
                 intent.getParcelableArrayListExtra(
diff --git a/core/java/com/android/internal/os/BatteryStatsHistory.java b/core/java/com/android/internal/os/BatteryStatsHistory.java
index b56aadd..c9c4be1 100644
--- a/core/java/com/android/internal/os/BatteryStatsHistory.java
+++ b/core/java/com/android/internal/os/BatteryStatsHistory.java
@@ -250,23 +250,22 @@
     private static class BatteryHistoryDirectory {
         private final File mDirectory;
         private final MonotonicClock mMonotonicClock;
-        private int mMaxHistoryFiles;
+        private int mMaxHistorySize;
         private final List<BatteryHistoryFile> mHistoryFiles = new ArrayList<>();
         private final ReentrantLock mLock = new ReentrantLock();
         private boolean mCleanupNeeded;
 
-        BatteryHistoryDirectory(File directory, MonotonicClock monotonicClock,
-                int maxHistoryFiles) {
+        BatteryHistoryDirectory(File directory, MonotonicClock monotonicClock, int maxHistorySize) {
             mDirectory = directory;
             mMonotonicClock = monotonicClock;
-            mMaxHistoryFiles = maxHistoryFiles;
-            if (mMaxHistoryFiles == 0) {
-                Slog.wtf(TAG, "mMaxHistoryFiles should not be zero when writing history");
+            mMaxHistorySize = maxHistorySize;
+            if (mMaxHistorySize == 0) {
+                Slog.w(TAG, "mMaxHistorySize should not be zero when writing history");
             }
         }
 
-        void setMaxHistoryFiles(int maxHistoryFiles) {
-            mMaxHistoryFiles = maxHistoryFiles;
+        void setMaxHistorySize(int maxHistorySize) {
+            mMaxHistorySize = maxHistorySize;
             cleanup();
         }
 
@@ -500,13 +499,14 @@
                     oldest.atomicFile.delete();
                 }
 
-                // if there are more history files than allowed, delete oldest history files.
-                // mMaxHistoryFiles comes from Constants.MAX_HISTORY_FILES and
-                // can be updated by DeviceConfig at run time.
-                while (mHistoryFiles.size() > mMaxHistoryFiles) {
+                // if there is more history stored than allowed, delete oldest history files.
+                int size = getSize();
+                while (size > mMaxHistorySize) {
                     BatteryHistoryFile oldest = mHistoryFiles.get(0);
+                    int length = (int) oldest.atomicFile.getBaseFile().length();
                     oldest.atomicFile.delete();
                     mHistoryFiles.remove(0);
+                    size -= length;
                 }
             } finally {
                 unlock();
@@ -595,19 +595,19 @@
      * Constructor
      *
      * @param systemDir            typically /data/system
-     * @param maxHistoryFiles      the largest number of history buffer files to keep
+     * @param maxHistorySize       the largest amount of battery history to keep on disk
      * @param maxHistoryBufferSize the most amount of RAM to used for buffering of history steps
      */
     public BatteryStatsHistory(Parcel historyBuffer, File systemDir,
-            int maxHistoryFiles, int maxHistoryBufferSize,
+            int maxHistorySize, int maxHistoryBufferSize,
             HistoryStepDetailsCalculator stepDetailsCalculator, Clock clock,
             MonotonicClock monotonicClock, TraceDelegate tracer, EventLogger eventLogger) {
-        this(historyBuffer, systemDir, maxHistoryFiles, maxHistoryBufferSize, stepDetailsCalculator,
+        this(historyBuffer, systemDir, maxHistorySize, maxHistoryBufferSize, stepDetailsCalculator,
                 clock, monotonicClock, tracer, eventLogger, null);
     }
 
     private BatteryStatsHistory(@Nullable Parcel historyBuffer, @Nullable File systemDir,
-            int maxHistoryFiles, int maxHistoryBufferSize,
+            int maxHistorySize, int maxHistoryBufferSize,
             @NonNull HistoryStepDetailsCalculator stepDetailsCalculator, @NonNull Clock clock,
             @NonNull MonotonicClock monotonicClock, @NonNull TraceDelegate tracer,
             @NonNull EventLogger eventLogger, @Nullable BatteryStatsHistory writableHistory) {
@@ -634,7 +634,7 @@
             mHistoryDir = writableHistory.mHistoryDir;
         } else if (systemDir != null) {
             mHistoryDir = new BatteryHistoryDirectory(new File(systemDir, HISTORY_DIR),
-                    monotonicClock, maxHistoryFiles);
+                    monotonicClock, maxHistorySize);
             mHistoryDir.load();
             BatteryHistoryFile activeFile = mHistoryDir.getLastFile();
             if (activeFile == null) {
@@ -690,11 +690,11 @@
     }
 
     /**
-     * Changes the maximum number of history files to be kept.
+     * Changes the maximum amount of history to be kept on disk.
      */
-    public void setMaxHistoryFiles(int maxHistoryFiles) {
+    public void setMaxHistorySize(int maxHistorySize) {
         if (mHistoryDir != null) {
-            mHistoryDir.setMaxHistoryFiles(maxHistoryFiles);
+            mHistoryDir.setMaxHistorySize(maxHistorySize);
         }
     }
 
@@ -1175,6 +1175,13 @@
     }
 
     /**
+     * Returns the maximum storage size allocated to battery history.
+     */
+    public int getMaxHistorySize() {
+        return mHistoryDir.mMaxHistorySize;
+    }
+
+    /**
      * @return the total size of all history files and history buffer.
      */
     public int getHistoryUsedSize() {
diff --git a/core/java/com/android/internal/os/logging/MetricsLoggerWrapper.java b/core/java/com/android/internal/os/logging/MetricsLoggerWrapper.java
index 0e0098e..efdc8ca 100644
--- a/core/java/com/android/internal/os/logging/MetricsLoggerWrapper.java
+++ b/core/java/com/android/internal/os/logging/MetricsLoggerWrapper.java
@@ -61,7 +61,7 @@
             return;
         }
         int pid = Process.myPid();
-        String processName = Application.getProcessName();
+        String processName = Process.myProcessName();
         Collection<NativeAllocationRegistry.Metrics> metrics =
             NativeAllocationRegistry.getMetrics();
         int nMetrics = metrics.size();
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 c160b42..5c08dc6 100644
--- a/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java
+++ b/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java
@@ -3133,9 +3133,9 @@
 
     private static ParseResult<ParsingPackage> parseAdoptPermissions(ParseInput input,
             ParsingPackage pkg, Resources res, XmlResourceParser parser) {
-        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestOriginalPackage);
+        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestAdoptPermissions);
         try {
-            String name = nonConfigString(0, R.styleable.AndroidManifestOriginalPackage_name, sa);
+            String name = nonConfigString(0, R.styleable.AndroidManifestAdoptPermissions_name, sa);
             if (name != null) {
                 pkg.addAdoptPermission(name);
             }
diff --git a/core/java/com/android/internal/vibrator/persistence/VibrationEffectXmlSerializer.java b/core/java/com/android/internal/vibrator/persistence/LegacyVibrationEffectXmlSerializer.java
similarity index 99%
rename from core/java/com/android/internal/vibrator/persistence/VibrationEffectXmlSerializer.java
rename to core/java/com/android/internal/vibrator/persistence/LegacyVibrationEffectXmlSerializer.java
index ebe3434..be30750 100644
--- a/core/java/com/android/internal/vibrator/persistence/VibrationEffectXmlSerializer.java
+++ b/core/java/com/android/internal/vibrator/persistence/LegacyVibrationEffectXmlSerializer.java
@@ -53,7 +53,7 @@
  *
  * @hide
  */
-public final class VibrationEffectXmlSerializer {
+public final class LegacyVibrationEffectXmlSerializer {
 
     /**
      * Creates a serialized representation of the input {@code vibration}.
diff --git a/core/java/com/android/internal/vibrator/persistence/SerializedAmplitudeStepWaveform.java b/core/java/com/android/internal/vibrator/persistence/SerializedAmplitudeStepWaveform.java
index cd7dcfd..efc7e35 100644
--- a/core/java/com/android/internal/vibrator/persistence/SerializedAmplitudeStepWaveform.java
+++ b/core/java/com/android/internal/vibrator/persistence/SerializedAmplitudeStepWaveform.java
@@ -35,6 +35,7 @@
 
 import java.io.IOException;
 import java.util.Arrays;
+import java.util.function.BiConsumer;
 
 /**
  * Serialized representation of a waveform effect created via
@@ -144,7 +145,7 @@
             // Read all nested tag that is not a repeating tag as a waveform entry.
             while (XmlReader.readNextTagWithin(parser, outerDepth)
                     && !TAG_REPEATING.equals(parser.getName())) {
-                parseWaveformEntry(parser, waveformBuilder);
+                parseWaveformEntry(parser, waveformBuilder::addDurationAndAmplitude);
             }
 
             // If found a repeating tag, read its content.
@@ -162,29 +163,8 @@
             return waveformBuilder.build();
         }
 
-        private static void parseRepeating(TypedXmlPullParser parser, Builder waveformBuilder)
-                throws XmlParserException, IOException {
-            XmlValidator.checkStartTag(parser, TAG_REPEATING);
-            XmlValidator.checkTagHasNoUnexpectedAttributes(parser);
-
-            waveformBuilder.setRepeatIndexToCurrentEntry();
-
-            boolean hasEntry = false;
-            int outerDepth = parser.getDepth();
-            while (XmlReader.readNextTagWithin(parser, outerDepth)) {
-                parseWaveformEntry(parser, waveformBuilder);
-                hasEntry = true;
-            }
-
-            // Check schema assertions about <repeating>
-            XmlValidator.checkParserCondition(hasEntry, "Unexpected empty %s tag", TAG_REPEATING);
-
-            // Consume tag
-            XmlReader.readEndTag(parser, TAG_REPEATING, outerDepth);
-        }
-
-        private static void parseWaveformEntry(TypedXmlPullParser parser, Builder waveformBuilder)
-                throws XmlParserException, IOException {
+        static void parseWaveformEntry(TypedXmlPullParser parser,
+                BiConsumer<Integer, Integer> builder) throws XmlParserException, IOException {
             XmlValidator.checkStartTag(parser, TAG_WAVEFORM_ENTRY);
             XmlValidator.checkTagHasNoUnexpectedAttributes(
                     parser, ATTRIBUTE_DURATION_MS, ATTRIBUTE_AMPLITUDE);
@@ -196,10 +176,31 @@
                             parser, ATTRIBUTE_AMPLITUDE, 0, VibrationEffect.MAX_AMPLITUDE);
             int durationMs = XmlReader.readAttributeIntNonNegative(parser, ATTRIBUTE_DURATION_MS);
 
-            waveformBuilder.addDurationAndAmplitude(durationMs, amplitude);
+            builder.accept(durationMs, amplitude);
 
             // Consume tag
             XmlReader.readEndTag(parser);
         }
+
+        private static void parseRepeating(TypedXmlPullParser parser, Builder waveformBuilder)
+                throws XmlParserException, IOException {
+            XmlValidator.checkStartTag(parser, TAG_REPEATING);
+            XmlValidator.checkTagHasNoUnexpectedAttributes(parser);
+
+            waveformBuilder.setRepeatIndexToCurrentEntry();
+
+            boolean hasEntry = false;
+            int outerDepth = parser.getDepth();
+            while (XmlReader.readNextTagWithin(parser, outerDepth)) {
+                parseWaveformEntry(parser, waveformBuilder::addDurationAndAmplitude);
+                hasEntry = true;
+            }
+
+            // Check schema assertions about <repeating>
+            XmlValidator.checkParserCondition(hasEntry, "Unexpected empty %s tag", TAG_REPEATING);
+
+            // Consume tag
+            XmlReader.readEndTag(parser, TAG_REPEATING, outerDepth);
+        }
     }
 }
diff --git a/core/java/com/android/internal/vibrator/persistence/SerializedRepeatingEffect.java b/core/java/com/android/internal/vibrator/persistence/SerializedRepeatingEffect.java
new file mode 100644
index 0000000..12acc72
--- /dev/null
+++ b/core/java/com/android/internal/vibrator/persistence/SerializedRepeatingEffect.java
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.vibrator.persistence;
+
+import static com.android.internal.vibrator.persistence.XmlConstants.NAMESPACE;
+import static com.android.internal.vibrator.persistence.XmlConstants.TAG_BASIC_ENVELOPE_EFFECT;
+import static com.android.internal.vibrator.persistence.XmlConstants.TAG_PREAMBLE;
+import static com.android.internal.vibrator.persistence.XmlConstants.TAG_PREDEFINED_EFFECT;
+import static com.android.internal.vibrator.persistence.XmlConstants.TAG_PRIMITIVE_EFFECT;
+import static com.android.internal.vibrator.persistence.XmlConstants.TAG_REPEATING;
+import static com.android.internal.vibrator.persistence.XmlConstants.TAG_REPEATING_EFFECT;
+import static com.android.internal.vibrator.persistence.XmlConstants.TAG_WAVEFORM_ENTRY;
+import static com.android.internal.vibrator.persistence.XmlConstants.TAG_WAVEFORM_ENVELOPE_EFFECT;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.VibrationEffect;
+
+import com.android.modules.utils.TypedXmlPullParser;
+import com.android.modules.utils.TypedXmlSerializer;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Serialized representation of a repeating effect created via
+ * {@link VibrationEffect#createRepeatingEffect}.
+ *
+ * @hide
+ */
+public class SerializedRepeatingEffect implements SerializedComposedEffect.SerializedSegment {
+
+    @Nullable
+    private final SerializedComposedEffect mSerializedPreamble;
+    @NonNull
+    private final SerializedComposedEffect mSerializedRepeating;
+
+    SerializedRepeatingEffect(@Nullable SerializedComposedEffect serializedPreamble,
+            @NonNull SerializedComposedEffect serializedRepeating) {
+        mSerializedPreamble = serializedPreamble;
+        mSerializedRepeating = serializedRepeating;
+    }
+
+    @Override
+    public void write(@NonNull TypedXmlSerializer serializer) throws IOException {
+        serializer.startTag(NAMESPACE, TAG_REPEATING_EFFECT);
+
+        if (mSerializedPreamble != null) {
+            serializer.startTag(NAMESPACE, TAG_PREAMBLE);
+            mSerializedPreamble.writeContent(serializer);
+            serializer.endTag(NAMESPACE, TAG_PREAMBLE);
+        }
+
+        serializer.startTag(NAMESPACE, TAG_REPEATING);
+        mSerializedRepeating.writeContent(serializer);
+        serializer.endTag(NAMESPACE, TAG_REPEATING);
+
+        serializer.endTag(NAMESPACE, TAG_REPEATING_EFFECT);
+    }
+
+    @Override
+    public void deserializeIntoComposition(@NonNull VibrationEffect.Composition composition) {
+        if (mSerializedPreamble != null) {
+            composition.addEffect(
+                    VibrationEffect.createRepeatingEffect(mSerializedPreamble.deserialize(),
+                            mSerializedRepeating.deserialize()));
+            return;
+        }
+
+        composition.addEffect(
+                VibrationEffect.createRepeatingEffect(mSerializedRepeating.deserialize()));
+    }
+
+    @Override
+    public String toString() {
+        return "SerializedRepeatingEffect{"
+                + "preamble=" + mSerializedPreamble
+                + ", repeating=" + mSerializedRepeating
+                + '}';
+    }
+
+    static final class Builder {
+        private SerializedComposedEffect mPreamble;
+        private SerializedComposedEffect mRepeating;
+
+        void setPreamble(SerializedComposedEffect effect) {
+            mPreamble = effect;
+        }
+
+        void setRepeating(SerializedComposedEffect effect) {
+            mRepeating = effect;
+        }
+
+        boolean hasRepeatingSegment() {
+            return mRepeating != null;
+        }
+
+        SerializedRepeatingEffect build() {
+            return new SerializedRepeatingEffect(mPreamble, mRepeating);
+        }
+    }
+
+    /** Parser implementation for {@link SerializedRepeatingEffect}. */
+    static final class Parser {
+
+        @NonNull
+        static SerializedRepeatingEffect parseNext(@NonNull TypedXmlPullParser parser,
+                @XmlConstants.Flags int flags) throws XmlParserException, IOException {
+            XmlValidator.checkStartTag(parser, TAG_REPEATING_EFFECT);
+            XmlValidator.checkTagHasNoUnexpectedAttributes(parser);
+
+            Builder builder = new Builder();
+            int outerDepth = parser.getDepth();
+
+            boolean hasNestedTag = XmlReader.readNextTagWithin(parser, outerDepth);
+            if (hasNestedTag && TAG_PREAMBLE.equals(parser.getName())) {
+                builder.setPreamble(parseEffect(parser, TAG_PREAMBLE, flags));
+                hasNestedTag = XmlReader.readNextTagWithin(parser, outerDepth);
+            }
+
+            XmlValidator.checkParserCondition(hasNestedTag,
+                    "Missing %s tag in %s", TAG_REPEATING, TAG_REPEATING_EFFECT);
+            builder.setRepeating(parseEffect(parser, TAG_REPEATING, flags));
+
+            XmlValidator.checkParserCondition(builder.hasRepeatingSegment(),
+                    "Unexpected %s tag with no repeating segment", TAG_REPEATING_EFFECT);
+
+            // Consume tag
+            XmlReader.readEndTag(parser, TAG_REPEATING_EFFECT, outerDepth);
+
+            return builder.build();
+        }
+
+        private static SerializedComposedEffect parseEffect(TypedXmlPullParser parser,
+                String tagName, int flags) throws XmlParserException, IOException {
+            XmlValidator.checkStartTag(parser, tagName);
+            XmlValidator.checkTagHasNoUnexpectedAttributes(parser);
+            int vibrationTagDepth = parser.getDepth();
+            XmlValidator.checkParserCondition(
+                    XmlReader.readNextTagWithin(parser, vibrationTagDepth),
+                    "Unsupported empty %s tag", tagName);
+
+            SerializedComposedEffect effect;
+            switch (parser.getName()) {
+                case TAG_PREDEFINED_EFFECT:
+                    effect = new SerializedComposedEffect(
+                            SerializedPredefinedEffect.Parser.parseNext(parser, flags));
+                    break;
+                case TAG_PRIMITIVE_EFFECT:
+                    effect = parsePrimitiveEffects(parser, vibrationTagDepth);
+                    break;
+                case TAG_WAVEFORM_ENTRY:
+                    effect = parseWaveformEntries(parser, vibrationTagDepth);
+                    break;
+                case TAG_WAVEFORM_ENVELOPE_EFFECT:
+                    effect = new SerializedComposedEffect(
+                            SerializedWaveformEnvelopeEffect.Parser.parseNext(parser, flags));
+                    break;
+                case TAG_BASIC_ENVELOPE_EFFECT:
+                    effect = new SerializedComposedEffect(
+                            SerializedBasicEnvelopeEffect.Parser.parseNext(parser, flags));
+                    break;
+                default:
+                    throw new XmlParserException("Unexpected tag " + parser.getName()
+                            + " in vibration tag " + tagName);
+            }
+
+            // Consume tag
+            XmlReader.readEndTag(parser, tagName, vibrationTagDepth);
+
+            return effect;
+        }
+
+        private static SerializedComposedEffect parsePrimitiveEffects(TypedXmlPullParser parser,
+                int vibrationTagDepth)
+                throws IOException, XmlParserException {
+            List<SerializedComposedEffect.SerializedSegment> primitives = new ArrayList<>();
+            do { // First primitive tag already open
+                primitives.add(SerializedCompositionPrimitive.Parser.parseNext(parser));
+            } while (XmlReader.readNextTagWithin(parser, vibrationTagDepth));
+            return new SerializedComposedEffect(primitives.toArray(
+                    new SerializedComposedEffect.SerializedSegment[
+                            primitives.size()]));
+        }
+
+        private static SerializedComposedEffect parseWaveformEntries(TypedXmlPullParser parser,
+                int vibrationTagDepth)
+                throws IOException, XmlParserException {
+            SerializedWaveformEffectEntries.Builder waveformBuilder =
+                    new SerializedWaveformEffectEntries.Builder();
+            do { // First waveform-entry tag already open
+                SerializedWaveformEffectEntries
+                        .Parser.parseWaveformEntry(parser, waveformBuilder);
+            } while (XmlReader.readNextTagWithin(parser, vibrationTagDepth));
+            XmlValidator.checkParserCondition(waveformBuilder.hasNonZeroDuration(),
+                    "Unexpected %s tag with total duration zero", TAG_WAVEFORM_ENTRY);
+            return new SerializedComposedEffect(waveformBuilder.build());
+        }
+    }
+}
diff --git a/core/java/com/android/internal/vibrator/persistence/SerializedWaveformEffectEntries.java b/core/java/com/android/internal/vibrator/persistence/SerializedWaveformEffectEntries.java
new file mode 100644
index 0000000..8849e75
--- /dev/null
+++ b/core/java/com/android/internal/vibrator/persistence/SerializedWaveformEffectEntries.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.vibrator.persistence;
+
+import static com.android.internal.vibrator.persistence.XmlConstants.ATTRIBUTE_AMPLITUDE;
+import static com.android.internal.vibrator.persistence.XmlConstants.ATTRIBUTE_DURATION_MS;
+import static com.android.internal.vibrator.persistence.XmlConstants.NAMESPACE;
+import static com.android.internal.vibrator.persistence.XmlConstants.TAG_WAVEFORM_ENTRY;
+import static com.android.internal.vibrator.persistence.XmlConstants.VALUE_AMPLITUDE_DEFAULT;
+
+import android.annotation.NonNull;
+import android.os.VibrationEffect;
+import android.util.IntArray;
+import android.util.LongArray;
+
+import com.android.internal.vibrator.persistence.SerializedComposedEffect.SerializedSegment;
+import com.android.modules.utils.TypedXmlPullParser;
+import com.android.modules.utils.TypedXmlSerializer;
+
+import java.io.IOException;
+import java.util.Arrays;
+
+/**
+ * Serialized representation of a list of waveform entries created via
+ * {@link VibrationEffect#createWaveform(long[], int[], int)}.
+ *
+ * @hide
+ */
+final class SerializedWaveformEffectEntries implements SerializedSegment {
+
+    @NonNull
+    private final long[] mTimings;
+    @NonNull
+    private final int[] mAmplitudes;
+
+    private SerializedWaveformEffectEntries(@NonNull long[] timings,
+            @NonNull int[] amplitudes) {
+        mTimings = timings;
+        mAmplitudes = amplitudes;
+    }
+
+    @Override
+    public void deserializeIntoComposition(@NonNull VibrationEffect.Composition composition) {
+        composition.addEffect(VibrationEffect.createWaveform(mTimings, mAmplitudes, -1));
+    }
+
+    @Override
+    public void write(@NonNull TypedXmlSerializer serializer) throws IOException {
+        for (int i = 0; i < mTimings.length; i++) {
+            serializer.startTag(NAMESPACE, TAG_WAVEFORM_ENTRY);
+
+            if (mAmplitudes[i] == VibrationEffect.DEFAULT_AMPLITUDE) {
+                serializer.attribute(NAMESPACE, ATTRIBUTE_AMPLITUDE, VALUE_AMPLITUDE_DEFAULT);
+            } else {
+                serializer.attributeInt(NAMESPACE, ATTRIBUTE_AMPLITUDE, mAmplitudes[i]);
+            }
+
+            serializer.attributeLong(NAMESPACE, ATTRIBUTE_DURATION_MS, mTimings[i]);
+            serializer.endTag(NAMESPACE, TAG_WAVEFORM_ENTRY);
+        }
+
+    }
+
+    @Override
+    public String toString() {
+        return "SerializedWaveformEffectEntries{"
+                + "timings=" + Arrays.toString(mTimings)
+                + ", amplitudes=" + Arrays.toString(mAmplitudes)
+                + '}';
+    }
+
+    /** Builder for {@link SerializedWaveformEffectEntries}. */
+    static final class Builder {
+        private final LongArray mTimings = new LongArray();
+        private final IntArray mAmplitudes = new IntArray();
+
+        void addDurationAndAmplitude(long durationMs, int amplitude) {
+            mTimings.add(durationMs);
+            mAmplitudes.add(amplitude);
+        }
+
+        boolean hasNonZeroDuration() {
+            for (int i = 0; i < mTimings.size(); i++) {
+                if (mTimings.get(i) > 0) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        SerializedWaveformEffectEntries build() {
+            return new SerializedWaveformEffectEntries(
+                    mTimings.toArray(), mAmplitudes.toArray());
+        }
+    }
+
+    /** Parser implementation for the {@link XmlConstants#TAG_WAVEFORM_ENTRY}. */
+    static final class Parser {
+
+        /** Parses a single {@link XmlConstants#TAG_WAVEFORM_ENTRY} into the builder. */
+        public static void parseWaveformEntry(TypedXmlPullParser parser, Builder waveformBuilder)
+                throws XmlParserException, IOException {
+            SerializedAmplitudeStepWaveform.Parser.parseWaveformEntry(parser,
+                    waveformBuilder::addDurationAndAmplitude);
+        }
+    }
+}
diff --git a/core/java/com/android/internal/vibrator/persistence/VibrationEffectSerializer.java b/core/java/com/android/internal/vibrator/persistence/VibrationEffectSerializer.java
new file mode 100644
index 0000000..df483ec
--- /dev/null
+++ b/core/java/com/android/internal/vibrator/persistence/VibrationEffectSerializer.java
@@ -0,0 +1,336 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.vibrator.persistence;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.PersistableBundle;
+import android.os.VibrationEffect;
+import android.os.vibrator.BasicPwleSegment;
+import android.os.vibrator.Flags;
+import android.os.vibrator.PrebakedSegment;
+import android.os.vibrator.PrimitiveSegment;
+import android.os.vibrator.PwleSegment;
+import android.os.vibrator.StepSegment;
+import android.os.vibrator.VibrationEffectSegment;
+
+import java.util.List;
+import java.util.function.BiConsumer;
+
+/**
+ * Serializer implementation for {@link VibrationEffect}.
+ *
+ * <p>This serializer does not support effects created with {@link VibrationEffect.WaveformBuilder}
+ * nor {@link VibrationEffect.Composition#addEffect(VibrationEffect)}. It only supports vibration
+ * effects defined as:
+ *
+ * <ul>
+ *     <li>{@link VibrationEffect#createPredefined(int)}
+ *     <li>{@link VibrationEffect#createWaveform(long[], int[], int)}
+ *     <li>A composition created exclusively via
+ *         {@link VibrationEffect.Composition#addPrimitive(int, float, int)}
+ *     <li>{@link VibrationEffect#createVendorEffect(PersistableBundle)}
+ *     <li>{@link VibrationEffect.WaveformEnvelopeBuilder}
+ *     <li>{@link VibrationEffect.BasicEnvelopeBuilder}
+ * </ul>
+ *
+ * <p>This serializer also supports repeating effects. For repeating waveform effects, it attempts
+ * to serialize the effect as a single unit. If this fails, it falls back to serializing it as a
+ * sequence of individual waveform entries.
+ *
+ * @hide
+ */
+public class VibrationEffectSerializer {
+    private static final String TAG = "VibrationEffectSerializer";
+
+    /**
+     * Creates a serialized representation of the input {@code vibration}.
+     */
+    @NonNull
+    public static XmlSerializedVibration<? extends VibrationEffect> serialize(
+            @NonNull VibrationEffect vibration, @XmlConstants.Flags int flags)
+            throws XmlSerializerException {
+
+        if (Flags.vendorVibrationEffects()
+                && (vibration instanceof VibrationEffect.VendorEffect vendorEffect)) {
+            return serializeVendorEffect(vendorEffect);
+        }
+
+        XmlValidator.checkSerializerCondition(vibration instanceof VibrationEffect.Composed,
+                "Unsupported VibrationEffect type %s", vibration);
+
+        VibrationEffect.Composed composed = (VibrationEffect.Composed) vibration;
+        XmlValidator.checkSerializerCondition(!composed.getSegments().isEmpty(),
+                "Unsupported empty VibrationEffect %s", vibration);
+
+        List<VibrationEffectSegment> segments = composed.getSegments();
+        int repeatIndex = composed.getRepeatIndex();
+
+        SerializedComposedEffect serializedEffect;
+        if (repeatIndex >= 0) {
+            serializedEffect = trySerializeRepeatingAmplitudeWaveformEffect(segments, repeatIndex);
+            if (serializedEffect == null) {
+                serializedEffect = serializeRepeatingEffect(segments, repeatIndex, flags);
+            }
+        } else {
+            serializedEffect = serializeNonRepeatingEffect(segments, flags);
+        }
+
+        return serializedEffect;
+    }
+
+    private static SerializedComposedEffect serializeRepeatingEffect(
+            List<VibrationEffectSegment> segments, int repeatIndex, @XmlConstants.Flags int flags)
+            throws XmlSerializerException {
+
+        SerializedRepeatingEffect.Builder builder = new SerializedRepeatingEffect.Builder();
+        if (repeatIndex > 0) {
+            List<VibrationEffectSegment> preambleSegments = segments.subList(0, repeatIndex);
+            builder.setPreamble(serializeEffectEntries(preambleSegments, flags));
+
+            // Update segments to match the repeating block only, after preamble was consumed.
+            segments = segments.subList(repeatIndex, segments.size());
+        }
+
+        builder.setRepeating(serializeEffectEntries(segments, flags));
+
+        return new SerializedComposedEffect(builder.build());
+    }
+
+    @NonNull
+    private static SerializedComposedEffect serializeNonRepeatingEffect(
+            List<VibrationEffectSegment> segments, @XmlConstants.Flags int flags)
+            throws XmlSerializerException {
+        SerializedComposedEffect effect = trySerializeNonWaveformEffect(segments, flags);
+        if (effect == null) {
+            effect = serializeWaveformEffect(segments);
+        }
+
+        return effect;
+    }
+
+    @NonNull
+    private static SerializedComposedEffect serializeEffectEntries(
+            List<VibrationEffectSegment> segments, @XmlConstants.Flags int flags)
+            throws XmlSerializerException {
+        SerializedComposedEffect effect = trySerializeNonWaveformEffect(segments, flags);
+        if (effect == null) {
+            effect = serializeWaveformEffectEntries(segments);
+        }
+
+        return effect;
+    }
+
+    @Nullable
+    private static SerializedComposedEffect trySerializeNonWaveformEffect(
+            List<VibrationEffectSegment> segments, int flags) throws XmlSerializerException {
+        VibrationEffectSegment firstSegment = segments.getFirst();
+
+        if (firstSegment instanceof PrebakedSegment) {
+            return serializePredefinedEffect(segments, flags);
+        }
+        if (firstSegment instanceof PrimitiveSegment) {
+            return serializePrimitiveEffect(segments);
+        }
+        if (firstSegment instanceof PwleSegment) {
+            return serializeWaveformEnvelopeEffect(segments);
+        }
+        if (firstSegment instanceof BasicPwleSegment) {
+            return serializeBasicEnvelopeEffect(segments);
+        }
+
+        return null;
+    }
+
+    private static SerializedComposedEffect serializePredefinedEffect(
+            List<VibrationEffectSegment> segments, @XmlConstants.Flags int flags)
+            throws XmlSerializerException {
+        XmlValidator.checkSerializerCondition(segments.size() == 1,
+                "Unsupported multiple segments in predefined effect: %s", segments);
+        return new SerializedComposedEffect(serializePrebakedSegment(segments.getFirst(), flags));
+    }
+
+    private static SerializedVendorEffect serializeVendorEffect(
+            VibrationEffect.VendorEffect effect) {
+        return new SerializedVendorEffect(effect.getVendorData());
+    }
+
+    private static SerializedComposedEffect serializePrimitiveEffect(
+            List<VibrationEffectSegment> segments) throws XmlSerializerException {
+        SerializedComposedEffect.SerializedSegment[] primitives =
+                new SerializedComposedEffect.SerializedSegment[segments.size()];
+        for (int i = 0; i < segments.size(); i++) {
+            primitives[i] = serializePrimitiveSegment(segments.get(i));
+        }
+
+        return new SerializedComposedEffect(primitives);
+    }
+
+    private static SerializedComposedEffect serializeWaveformEnvelopeEffect(
+            List<VibrationEffectSegment> segments) throws XmlSerializerException {
+        SerializedWaveformEnvelopeEffect.Builder builder =
+                new SerializedWaveformEnvelopeEffect.Builder();
+        for (int i = 0; i < segments.size(); i++) {
+            XmlValidator.checkSerializerCondition(segments.get(i) instanceof PwleSegment,
+                    "Unsupported segment for waveform envelope effect %s", segments.get(i));
+            PwleSegment segment = (PwleSegment) segments.get(i);
+
+            if (i == 0 && segment.getStartFrequencyHz() != segment.getEndFrequencyHz()) {
+                // Initial frequency explicitly defined.
+                builder.setInitialFrequencyHz(segment.getStartFrequencyHz());
+            }
+
+            builder.addControlPoint(segment.getEndAmplitude(), segment.getEndFrequencyHz(),
+                    segment.getDuration());
+        }
+
+        return new SerializedComposedEffect(builder.build());
+    }
+
+    private static SerializedComposedEffect serializeBasicEnvelopeEffect(
+            List<VibrationEffectSegment> segments) throws XmlSerializerException {
+        SerializedBasicEnvelopeEffect.Builder builder = new SerializedBasicEnvelopeEffect.Builder();
+        for (int i = 0; i < segments.size(); i++) {
+            XmlValidator.checkSerializerCondition(segments.get(i) instanceof BasicPwleSegment,
+                    "Unsupported segment for basic envelope effect %s", segments.get(i));
+            BasicPwleSegment segment = (BasicPwleSegment) segments.get(i);
+
+            if (i == 0 && segment.getStartSharpness() != segment.getEndSharpness()) {
+                // Initial sharpness explicitly defined.
+                builder.setInitialSharpness(segment.getStartSharpness());
+            }
+
+            builder.addControlPoint(segment.getEndIntensity(), segment.getEndSharpness(),
+                    segment.getDuration());
+        }
+
+        return new SerializedComposedEffect(builder.build());
+    }
+
+    private static SerializedComposedEffect trySerializeRepeatingAmplitudeWaveformEffect(
+            List<VibrationEffectSegment> segments, int repeatingIndex) {
+        SerializedAmplitudeStepWaveform.Builder builder =
+                new SerializedAmplitudeStepWaveform.Builder();
+
+        for (int i = 0; i < segments.size(); i++) {
+            if (repeatingIndex == i) {
+                builder.setRepeatIndexToCurrentEntry();
+            }
+            try {
+                serializeStepSegment(segments.get(i), builder::addDurationAndAmplitude);
+            } catch (XmlSerializerException e) {
+                return null;
+            }
+        }
+
+        return new SerializedComposedEffect(builder.build());
+    }
+
+    private static SerializedComposedEffect serializeWaveformEffect(
+            List<VibrationEffectSegment> segments) throws XmlSerializerException {
+        SerializedAmplitudeStepWaveform.Builder builder =
+                new SerializedAmplitudeStepWaveform.Builder();
+        for (int i = 0; i < segments.size(); i++) {
+            serializeStepSegment(segments.get(i), builder::addDurationAndAmplitude);
+        }
+
+        return new SerializedComposedEffect(builder.build());
+    }
+
+    private static SerializedComposedEffect serializeWaveformEffectEntries(
+            List<VibrationEffectSegment> segments) throws XmlSerializerException {
+        SerializedWaveformEffectEntries.Builder builder =
+                new SerializedWaveformEffectEntries.Builder();
+        for (int i = 0; i < segments.size(); i++) {
+            serializeStepSegment(segments.get(i), builder::addDurationAndAmplitude);
+        }
+
+        return new SerializedComposedEffect(builder.build());
+    }
+
+    private static void serializeStepSegment(VibrationEffectSegment segment,
+            BiConsumer<Long, Integer> builder) throws XmlSerializerException {
+        XmlValidator.checkSerializerCondition(segment instanceof StepSegment,
+                "Unsupported segment for waveform effect %s", segment);
+
+        XmlValidator.checkSerializerCondition(
+                Float.compare(((StepSegment) segment).getFrequencyHz(), 0) == 0,
+                "Unsupported segment with non-default frequency %f",
+                ((StepSegment) segment).getFrequencyHz());
+
+        builder.accept(segment.getDuration(),
+                toAmplitudeInt(((StepSegment) segment).getAmplitude()));
+    }
+
+    private static SerializedPredefinedEffect serializePrebakedSegment(
+            VibrationEffectSegment segment, @XmlConstants.Flags int flags)
+            throws XmlSerializerException {
+        XmlValidator.checkSerializerCondition(segment instanceof PrebakedSegment,
+                "Unsupported segment for predefined effect %s", segment);
+
+        PrebakedSegment prebaked = (PrebakedSegment) segment;
+        XmlConstants.PredefinedEffectName effectName = XmlConstants.PredefinedEffectName.findById(
+                prebaked.getEffectId(), flags);
+
+        XmlValidator.checkSerializerCondition(effectName != null,
+                "Unsupported predefined effect id %s", prebaked.getEffectId());
+
+        if ((flags & XmlConstants.FLAG_ALLOW_HIDDEN_APIS) == 0) {
+            // Only allow effects with default fallback flag if using the public APIs schema.
+            XmlValidator.checkSerializerCondition(
+                    prebaked.shouldFallback() == PrebakedSegment.DEFAULT_SHOULD_FALLBACK,
+                    "Unsupported predefined effect with should fallback %s",
+                    prebaked.shouldFallback());
+        }
+
+        return new SerializedPredefinedEffect(effectName, prebaked.shouldFallback());
+    }
+
+    private static SerializedCompositionPrimitive serializePrimitiveSegment(
+            VibrationEffectSegment segment) throws XmlSerializerException {
+        XmlValidator.checkSerializerCondition(segment instanceof PrimitiveSegment,
+                "Unsupported segment for primitive composition %s", segment);
+
+        PrimitiveSegment primitive = (PrimitiveSegment) segment;
+        XmlConstants.PrimitiveEffectName primitiveName =
+                XmlConstants.PrimitiveEffectName.findById(primitive.getPrimitiveId());
+
+        XmlValidator.checkSerializerCondition(primitiveName != null,
+                "Unsupported primitive effect id %s", primitive.getPrimitiveId());
+
+        XmlConstants.PrimitiveDelayType delayType = null;
+
+        if (Flags.primitiveCompositionAbsoluteDelay()) {
+            delayType = XmlConstants.PrimitiveDelayType.findByType(primitive.getDelayType());
+            XmlValidator.checkSerializerCondition(delayType != null,
+                    "Unsupported primitive delay type %s", primitive.getDelayType());
+        } else {
+            XmlValidator.checkSerializerCondition(
+                    primitive.getDelayType() == PrimitiveSegment.DEFAULT_DELAY_TYPE,
+                    "Unsupported primitive delay type %s", primitive.getDelayType());
+        }
+
+        return new SerializedCompositionPrimitive(
+                primitiveName, primitive.getScale(), primitive.getDelay(), delayType);
+    }
+
+    private static int toAmplitudeInt(float amplitude) {
+        return Float.compare(amplitude, VibrationEffect.DEFAULT_AMPLITUDE) == 0
+                ? VibrationEffect.DEFAULT_AMPLITUDE
+                : Math.round(amplitude * VibrationEffect.MAX_AMPLITUDE);
+    }
+}
diff --git a/core/java/com/android/internal/vibrator/persistence/VibrationEffectXmlParser.java b/core/java/com/android/internal/vibrator/persistence/VibrationEffectXmlParser.java
index 314bfe4..efd75fc 100644
--- a/core/java/com/android/internal/vibrator/persistence/VibrationEffectXmlParser.java
+++ b/core/java/com/android/internal/vibrator/persistence/VibrationEffectXmlParser.java
@@ -19,6 +19,7 @@
 import static com.android.internal.vibrator.persistence.XmlConstants.TAG_BASIC_ENVELOPE_EFFECT;
 import static com.android.internal.vibrator.persistence.XmlConstants.TAG_PREDEFINED_EFFECT;
 import static com.android.internal.vibrator.persistence.XmlConstants.TAG_PRIMITIVE_EFFECT;
+import static com.android.internal.vibrator.persistence.XmlConstants.TAG_REPEATING_EFFECT;
 import static com.android.internal.vibrator.persistence.XmlConstants.TAG_VENDOR_EFFECT;
 import static com.android.internal.vibrator.persistence.XmlConstants.TAG_VIBRATION_EFFECT;
 import static com.android.internal.vibrator.persistence.XmlConstants.TAG_WAVEFORM_EFFECT;
@@ -120,6 +121,26 @@
  *     }
  * </pre>
  *
+ * * Repeating effects
+ *
+ * <pre>
+ *     {@code
+ *       <vibration-effect>
+ *          <repeating-effect>
+ *            <preamble>
+ *                <primitive-effect name="click" />
+ *            </preamble>
+ *            <repeating>
+ *              <basic-envelope-effect>
+ *                <control-point intensity="0.3" sharpness="0.4" durationMs="25" />
+ *                <control-point intensity="0.0" sharpness="0.5" durationMs="30" />
+ *              </basic-envelope-effect>
+ *            </repeating>
+ *          </repeating-effect>
+ *       </vibration-effect>
+ *     }
+ * </pre>
+ *
  * @hide
  */
 public class VibrationEffectXmlParser {
@@ -191,6 +212,12 @@
                             SerializedBasicEnvelopeEffect.Parser.parseNext(parser, flags));
                     break;
                 } // else fall through
+            case TAG_REPEATING_EFFECT:
+                if (Flags.normalizedPwleEffects()) {
+                    serializedVibration = new SerializedComposedEffect(
+                            SerializedRepeatingEffect.Parser.parseNext(parser, flags));
+                    break;
+                } // else fall through
             default:
                 throw new XmlParserException("Unexpected tag " + parser.getName()
                         + " in vibration tag " + vibrationTagName);
diff --git a/core/java/com/android/internal/vibrator/persistence/XmlConstants.java b/core/java/com/android/internal/vibrator/persistence/XmlConstants.java
index df262cf..cc5c7cf 100644
--- a/core/java/com/android/internal/vibrator/persistence/XmlConstants.java
+++ b/core/java/com/android/internal/vibrator/persistence/XmlConstants.java
@@ -45,8 +45,10 @@
     public static final String TAG_WAVEFORM_ENVELOPE_EFFECT = "waveform-envelope-effect";
     public static final String TAG_BASIC_ENVELOPE_EFFECT = "basic-envelope-effect";
     public static final String TAG_WAVEFORM_EFFECT = "waveform-effect";
+    public static final String TAG_REPEATING_EFFECT = "repeating-effect";
     public static final String TAG_WAVEFORM_ENTRY = "waveform-entry";
     public static final String TAG_REPEATING = "repeating";
+    public static final String TAG_PREAMBLE = "preamble";
     public static final String TAG_CONTROL_POINT = "control-point";
 
     public static final String ATTRIBUTE_NAME = "name";
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 19c6f51..9bd5237 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -1326,7 +1326,7 @@
         try {
             getLockSettings().registerStrongAuthTracker(strongAuthTracker.getStub());
         } catch (RemoteException e) {
-            throw new RuntimeException("Could not register StrongAuthTracker");
+            e.rethrowFromSystemServer();
         }
     }
 
diff --git a/core/java/com/android/internal/widget/MessagingGroup.java b/core/java/com/android/internal/widget/MessagingGroup.java
index 4305ba7..31d9770 100644
--- a/core/java/com/android/internal/widget/MessagingGroup.java
+++ b/core/java/com/android/internal/widget/MessagingGroup.java
@@ -81,7 +81,6 @@
     private MessagingLinearLayout mMessageContainer;
     ImageFloatingTextView mSenderView;
     private ImageView mAvatarView;
-    private View mAvatarContainer;
     private String mAvatarSymbol = "";
     private int mLayoutColor;
     private CharSequence mAvatarName = "";
@@ -449,6 +448,17 @@
         mSenderView.setVisibility(hidden ? GONE : VISIBLE);
     }
 
+    private void updateIconVisibility() {
+        if (Flags.notificationsRedesignTemplates() && !mIsInConversation) {
+            // We don't show any icon (other than the app icon) in the collapsed form. For
+            // conversations, keeping this container helps with aligning the message to the icon
+            // when collapsed, but the old messaging style already has this alignment built into
+            // the template like all other layouts. Conversations are special because we use the
+            // same base layout for both the collapsed and expanded views.
+            mMessagingIconContainer.setVisibility(mSingleLine ? GONE : VISIBLE);
+        }
+    }
+
     @Override
     public boolean hasDifferentHeightWhenFirst() {
         return mCanHideSenderIfFirst && !mSingleLine && !TextUtils.isEmpty(mSenderName);
@@ -704,6 +714,7 @@
             updateMaxDisplayedLines();
             updateClipRect();
             updateSenderVisibility();
+            updateIconVisibility();
         }
     }
 
@@ -719,6 +730,14 @@
     public void setIsInConversation(boolean isInConversation) {
         if (mIsInConversation != isInConversation) {
             mIsInConversation = isInConversation;
+
+            if (Flags.notificationsRedesignTemplates()) {
+                updateIconVisibility();
+                // No other alignment adjustments are necessary in the redesign, as the size of the
+                // icons in both conversations and old messaging notifications are the same.
+                return;
+            }
+
             MarginLayoutParams layoutParams =
                     (MarginLayoutParams) mMessagingIconContainer.getLayoutParams();
             layoutParams.width = mIsInConversation
diff --git a/core/java/com/android/internal/widget/NotificationProgressDrawable.java b/core/java/com/android/internal/widget/NotificationProgressDrawable.java
index e95225e..8629a1c 100644
--- a/core/java/com/android/internal/widget/NotificationProgressDrawable.java
+++ b/core/java/com/android/internal/widget/NotificationProgressDrawable.java
@@ -147,11 +147,6 @@
             final Part prevPart = iPart == 0 ? null : mParts.get(iPart - 1);
             final Part nextPart = iPart + 1 == numParts ? null : mParts.get(iPart + 1);
             if (part instanceof Segment segment) {
-                // Update the segment-point gap to 2X upon seeing the first faded segment.
-                // (Assuming that all segments before are solid, and all segments after are faded.)
-                if (segment.mFaded) {
-                    segPointGap = mState.mSegPointGap * 2;
-                }
                 final float segWidth = segment.mFraction * totalWidth;
                 // Advance the start position to account for a point immediately prior.
                 final float startOffset = getSegStartOffset(prevPart, pointRadius, segPointGap, x);
@@ -225,7 +220,7 @@
         if (nextPart instanceof Segment nextSeg) {
             if (!seg.mFaded && nextSeg.mFaded) {
                 // @see Segment#mFaded
-                return hasTrackerIcon ? 0F : segSegGap * 4F;
+                return hasTrackerIcon ? 0F : segSegGap;
             }
             return segSegGap;
         }
@@ -487,11 +482,10 @@
          * <p>
          *     <pre>
          *     When mFaded is set to true, a combination of the following is done to the segment:
-         *       1. The drawing color is mColor with opacity updated to 15%.
-         *       2. The segment-point gap is 2X the segment-point gap for non-faded segments.
-         *       3. The gap between faded and non-faded segments is:
-         *          4X the segment-segment gap, when there is no tracker icon
-         *          0, when there is tracker icon
+         *       1. The drawing color is mColor with opacity updated to 40%.
+         *       2. The gap between faded and non-faded segments is:
+         *          - the segment-segment gap, when there is no tracker icon
+         *          - 0, when there is tracker icon
          *     </pre>
          * </p>
          */
@@ -764,7 +758,7 @@
     @ColorInt
     static int getFadedColor(@ColorInt int color) {
         return Color.argb(
-                (int) (Color.alpha(color) * 0.25f + 0.5f),
+                (int) (Color.alpha(color) * 0.4f + 0.5f),
                 Color.red(color),
                 Color.green(color),
                 Color.blue(color));
diff --git a/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java b/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java
index 33f93fc..60767ed 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java
@@ -21,26 +21,20 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.widget.remotecompose.core.operations.ComponentValue;
 import com.android.internal.widget.remotecompose.core.operations.FloatExpression;
+import com.android.internal.widget.remotecompose.core.operations.Header;
 import com.android.internal.widget.remotecompose.core.operations.IntegerExpression;
 import com.android.internal.widget.remotecompose.core.operations.NamedVariable;
 import com.android.internal.widget.remotecompose.core.operations.RootContentBehavior;
 import com.android.internal.widget.remotecompose.core.operations.ShaderData;
 import com.android.internal.widget.remotecompose.core.operations.TextData;
 import com.android.internal.widget.remotecompose.core.operations.Theme;
-import com.android.internal.widget.remotecompose.core.operations.layout.ClickModifierOperation;
 import com.android.internal.widget.remotecompose.core.operations.layout.Component;
-import com.android.internal.widget.remotecompose.core.operations.layout.ComponentEnd;
-import com.android.internal.widget.remotecompose.core.operations.layout.ComponentStartOperation;
-import com.android.internal.widget.remotecompose.core.operations.layout.LoopEnd;
+import com.android.internal.widget.remotecompose.core.operations.layout.Container;
+import com.android.internal.widget.remotecompose.core.operations.layout.ContainerEnd;
 import com.android.internal.widget.remotecompose.core.operations.layout.LoopOperation;
-import com.android.internal.widget.remotecompose.core.operations.layout.OperationsListEnd;
 import com.android.internal.widget.remotecompose.core.operations.layout.RootLayoutComponent;
-import com.android.internal.widget.remotecompose.core.operations.layout.TouchCancelModifierOperation;
-import com.android.internal.widget.remotecompose.core.operations.layout.TouchDownModifierOperation;
-import com.android.internal.widget.remotecompose.core.operations.layout.TouchUpModifierOperation;
 import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ComponentModifiers;
 import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ModifierOperation;
-import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ScrollModifierOperation;
 import com.android.internal.widget.remotecompose.core.operations.utilities.StringSerializer;
 
 import java.util.ArrayList;
@@ -57,7 +51,18 @@
 public class CoreDocument {
 
     private static final boolean DEBUG = false;
-    private static final int DOCUMENT_API_LEVEL = 2;
+
+    // Semantic version
+    public static final int MAJOR_VERSION = 0;
+    public static final int MINOR_VERSION = 3;
+    public static final int PATCH_VERSION = 0;
+
+    // Internal version level
+    public static final int DOCUMENT_API_LEVEL = 3;
+
+    // We also keep a more fine-grained BUILD number, exposed as
+    // ID_API_LEVEL = DOCUMENT_API_LEVEL + BUILD
+    static final float BUILD = 0.0f;
 
     @NonNull ArrayList<Operation> mOperations = new ArrayList<>();
 
@@ -65,8 +70,9 @@
 
     @NonNull RemoteComposeState mRemoteComposeState = new RemoteComposeState();
     @VisibleForTesting @NonNull public TimeVariables mTimeVariables = new TimeVariables();
+
     // Semantic version of the document
-    @NonNull Version mVersion = new Version(0, 1, 0);
+    @NonNull Version mVersion = new Version(MAJOR_VERSION, MINOR_VERSION, PATCH_VERSION);
 
     @Nullable
     String mContentDescription; // text description of the document (used for accessibility)
@@ -551,6 +557,11 @@
         mOperations = new ArrayList<Operation>();
         buffer.inflateFromBuffer(mOperations);
         for (Operation op : mOperations) {
+            if (op instanceof Header) {
+                // Make sure we parse the version at init time...
+                Header header = (Header) op;
+                header.setVersion(this);
+            }
             if (op instanceof IntegerExpression) {
                 IntegerExpression expression = (IntegerExpression) op;
                 mIntegerExpressions.put((long) expression.mId, expression);
@@ -581,85 +592,55 @@
      */
     @NonNull
     private ArrayList<Operation> inflateComponents(@NonNull ArrayList<Operation> operations) {
-        Component currentComponent = null;
-        ArrayList<Component> components = new ArrayList<>();
         ArrayList<Operation> finalOperationsList = new ArrayList<>();
         ArrayList<Operation> ops = finalOperationsList;
-        ClickModifierOperation currentClickModifier = null;
-        TouchDownModifierOperation currentTouchDownModifier = null;
-        TouchUpModifierOperation currentTouchUpModifier = null;
-        TouchCancelModifierOperation currentTouchCancelModifier = null;
-        LoopOperation currentLoop = null;
-        ScrollModifierOperation currentScrollModifier = null;
+
+        ArrayList<Container> containers = new ArrayList<>();
 
         mLastId = -1;
         for (Operation o : operations) {
-            if (o instanceof ComponentStartOperation) {
-                Component component = (Component) o;
-                component.setParent(currentComponent);
-                components.add(component);
-                currentComponent = component;
-                ops.add(currentComponent);
-                ops = currentComponent.getList();
-                if (component.getComponentId() < mLastId) {
-                    mLastId = component.getComponentId();
+            if (o instanceof Container) {
+                Container container = (Container) o;
+                if (container instanceof Component) {
+                    Component component = (Component) container;
+                    // Make sure to set the parent when a component is first found, so that
+                    // the inflate when closing the component is in a state where the hierarchy
+                    // is already existing.
+                    if (!containers.isEmpty()) {
+                        Container parentContainer = containers.get(containers.size() - 1);
+                        if (parentContainer instanceof Component) {
+                            component.setParent((Component) parentContainer);
+                        }
+                    }
+                    if (component.getComponentId() < mLastId) {
+                        mLastId = component.getComponentId();
+                    }
                 }
-            } else if (o instanceof ComponentEnd) {
-                if (currentComponent != null) {
-                    currentComponent.inflate();
+                containers.add(container);
+                ops = container.getList();
+            } else if (o instanceof ContainerEnd) {
+                // check if we have a parent container
+                Container container = null;
+                // pop the container
+                if (!containers.isEmpty()) {
+                    container = containers.remove(containers.size() - 1);
                 }
-                components.remove(components.size() - 1);
-                if (!components.isEmpty()) {
-                    currentComponent = components.get(components.size() - 1);
-                    ops = currentComponent.getList();
+                Container parentContainer = null;
+                if (!containers.isEmpty()) {
+                    parentContainer = containers.get(containers.size() - 1);
+                }
+                if (parentContainer != null) {
+                    ops = parentContainer.getList();
                 } else {
                     ops = finalOperationsList;
                 }
-            } else if (o instanceof ClickModifierOperation) {
-                // TODO: refactor to add container <- component...
-                currentClickModifier = (ClickModifierOperation) o;
-                ops = currentClickModifier.getList();
-            } else if (o instanceof TouchDownModifierOperation) {
-                currentTouchDownModifier = (TouchDownModifierOperation) o;
-                ops = currentTouchDownModifier.getList();
-            } else if (o instanceof TouchUpModifierOperation) {
-                currentTouchUpModifier = (TouchUpModifierOperation) o;
-                ops = currentTouchUpModifier.getList();
-            } else if (o instanceof TouchCancelModifierOperation) {
-                currentTouchCancelModifier = (TouchCancelModifierOperation) o;
-                ops = currentTouchCancelModifier.getList();
-            } else if (o instanceof ScrollModifierOperation) {
-                currentScrollModifier = (ScrollModifierOperation) o;
-                ops = currentScrollModifier.getList();
-            } else if (o instanceof OperationsListEnd) {
-                ops = currentComponent.getList();
-                if (currentClickModifier != null) {
-                    ops.add(currentClickModifier);
-                    currentClickModifier = null;
-                } else if (currentTouchDownModifier != null) {
-                    ops.add(currentTouchDownModifier);
-                    currentTouchDownModifier = null;
-                } else if (currentTouchUpModifier != null) {
-                    ops.add(currentTouchUpModifier);
-                    currentTouchUpModifier = null;
-                } else if (currentTouchCancelModifier != null) {
-                    ops.add(currentTouchCancelModifier);
-                    currentTouchCancelModifier = null;
-                } else if (currentScrollModifier != null) {
-                    ops.add(currentScrollModifier);
-                    currentScrollModifier = null;
+                if (container != null) {
+                    if (container instanceof Component) {
+                        Component component = (Component) container;
+                        component.inflate();
+                    }
+                    ops.add((Operation) container);
                 }
-            } else if (o instanceof LoopOperation) {
-                currentLoop = (LoopOperation) o;
-                ops = currentLoop.getList();
-            } else if (o instanceof LoopEnd) {
-                if (currentComponent != null) {
-                    ops = currentComponent.getList();
-                    ops.add(currentLoop);
-                } else {
-                    ops = finalOperationsList;
-                }
-                currentLoop = null;
             } else {
                 ops.add(o);
             }
@@ -744,7 +725,7 @@
      * @param minorVersion minor version number, increased when adding new features
      * @param patch patch level, increased upon bugfixes
      */
-    void setVersion(int majorVersion, int minorVersion, int patch) {
+    public void setVersion(int majorVersion, int minorVersion, int patch) {
         mVersion = new Version(majorVersion, minorVersion, patch);
     }
 
@@ -1080,19 +1061,22 @@
             }
         }
         context.mMode = RemoteContext.ContextMode.PAINT;
-        for (Operation op : mOperations) {
+        for (int i = 0; i < mOperations.size(); i++) {
+            Operation op = mOperations.get(i);
             // operations will only be executed if no theme is set (ie UNSPECIFIED)
             // or the theme is equal as the one passed in argument to paint.
             boolean apply = true;
             if (theme != Theme.UNSPECIFIED) {
+                int currentTheme = context.getTheme();
                 apply =
-                        op instanceof Theme // always apply a theme setter
-                                || context.getTheme() == theme
-                                || context.getTheme() == Theme.UNSPECIFIED;
+                        currentTheme == theme
+                                || currentTheme == Theme.UNSPECIFIED
+                                || op instanceof Theme; // always apply a theme setter
             }
             if (apply) {
-                if (op.isDirty() || op instanceof PaintOperation) {
-                    if (op.isDirty() && op instanceof VariableSupport) {
+                boolean opIsDirty = op.isDirty();
+                if (opIsDirty || op instanceof PaintOperation) {
+                    if (opIsDirty && op instanceof VariableSupport) {
                         op.markNotDirty();
                         ((VariableSupport) op).updateVariables(context);
                     }
@@ -1253,8 +1237,11 @@
     private void toNestedString(
             @NonNull Component base, @NonNull StringBuilder ret, String indent) {
         for (Operation mOperation : base.mList) {
-            ret.append(mOperation.toString());
-            ret.append("\n");
+            for (String line : mOperation.toString().split("\n")) {
+                ret.append(indent);
+                ret.append(line);
+                ret.append("\n");
+            }
             if (mOperation instanceof Component) {
                 toNestedString((Component) mOperation, ret, indent + "  ");
             }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/Operations.java b/core/java/com/android/internal/widget/remotecompose/core/Operations.java
index 04e490f..d9f12cb 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/Operations.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/Operations.java
@@ -73,12 +73,10 @@
 import com.android.internal.widget.remotecompose.core.operations.TouchExpression;
 import com.android.internal.widget.remotecompose.core.operations.layout.CanvasContent;
 import com.android.internal.widget.remotecompose.core.operations.layout.ClickModifierOperation;
-import com.android.internal.widget.remotecompose.core.operations.layout.ComponentEnd;
 import com.android.internal.widget.remotecompose.core.operations.layout.ComponentStart;
+import com.android.internal.widget.remotecompose.core.operations.layout.ContainerEnd;
 import com.android.internal.widget.remotecompose.core.operations.layout.LayoutComponentContent;
-import com.android.internal.widget.remotecompose.core.operations.layout.LoopEnd;
 import com.android.internal.widget.remotecompose.core.operations.layout.LoopOperation;
-import com.android.internal.widget.remotecompose.core.operations.layout.OperationsListEnd;
 import com.android.internal.widget.remotecompose.core.operations.layout.RootLayoutComponent;
 import com.android.internal.widget.remotecompose.core.operations.layout.TouchCancelModifierOperation;
 import com.android.internal.widget.remotecompose.core.operations.layout.TouchDownModifierOperation;
@@ -208,7 +206,6 @@
     public static final int LAYOUT_STATE = 217;
 
     public static final int COMPONENT_START = 2;
-    public static final int COMPONENT_END = 3;
 
     public static final int MODIFIER_WIDTH = 16;
     public static final int MODIFIER_HEIGHT = 67;
@@ -223,7 +220,7 @@
     public static final int MODIFIER_TOUCH_UP = 220;
     public static final int MODIFIER_TOUCH_CANCEL = 225;
 
-    public static final int OPERATIONS_LIST_END = 214;
+    public static final int CONTAINER_END = 214;
 
     public static final int MODIFIER_OFFSET = 221;
     public static final int MODIFIER_ZINDEX = 223;
@@ -233,7 +230,6 @@
     public static final int MODIFIER_RIPPLE = 229;
 
     public static final int LOOP_START = 215;
-    public static final int LOOP_END = 216;
 
     public static final int MODIFIER_VISIBILITY = 211;
     public static final int HOST_ACTION = 209;
@@ -311,12 +307,10 @@
         map.put(TEXT_LOOKUP_INT, TextLookupInt::read);
 
         map.put(LOOP_START, LoopOperation::read);
-        map.put(LOOP_END, LoopEnd::read);
 
         // Layout
 
         map.put(COMPONENT_START, ComponentStart::read);
-        map.put(COMPONENT_END, ComponentEnd::read);
         map.put(ANIMATION_SPEC, AnimationSpec::read);
 
         map.put(MODIFIER_WIDTH, WidthModifierOperation::read);
@@ -338,7 +332,7 @@
         map.put(MODIFIER_MARQUEE, MarqueeModifierOperation::read);
         map.put(MODIFIER_RIPPLE, RippleModifierOperation::read);
 
-        map.put(OPERATIONS_LIST_END, OperationsListEnd::read);
+        map.put(CONTAINER_END, ContainerEnd::read);
 
         map.put(HOST_ACTION, HostActionOperation::read);
         map.put(HOST_NAMED_ACTION, HostNamedActionOperation::read);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/PaintContext.java b/core/java/com/android/internal/widget/remotecompose/core/PaintContext.java
index 7ecd118..4a40a31 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/PaintContext.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/PaintContext.java
@@ -16,6 +16,7 @@
 package com.android.internal.widget.remotecompose.core;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 
 import com.android.internal.widget.remotecompose.core.operations.paint.PaintBundle;
 
@@ -297,4 +298,12 @@
     public boolean isVisualDebug() {
         return mContext.isVisualDebug();
     }
+
+    /**
+     * Returns a String from an id
+     *
+     * @param textID
+     * @return the string if found
+     */
+    public abstract @Nullable String getText(int textID);
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java
index 0ae7a94..fc1e36d 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java
@@ -75,12 +75,10 @@
 import com.android.internal.widget.remotecompose.core.operations.TouchExpression;
 import com.android.internal.widget.remotecompose.core.operations.Utils;
 import com.android.internal.widget.remotecompose.core.operations.layout.CanvasContent;
-import com.android.internal.widget.remotecompose.core.operations.layout.ComponentEnd;
 import com.android.internal.widget.remotecompose.core.operations.layout.ComponentStart;
+import com.android.internal.widget.remotecompose.core.operations.layout.ContainerEnd;
 import com.android.internal.widget.remotecompose.core.operations.layout.LayoutComponentContent;
-import com.android.internal.widget.remotecompose.core.operations.layout.LoopEnd;
 import com.android.internal.widget.remotecompose.core.operations.layout.LoopOperation;
-import com.android.internal.widget.remotecompose.core.operations.layout.OperationsListEnd;
 import com.android.internal.widget.remotecompose.core.operations.layout.RootLayoutComponent;
 import com.android.internal.widget.remotecompose.core.operations.layout.managers.BoxLayout;
 import com.android.internal.widget.remotecompose.core.operations.layout.managers.CanvasLayout;
@@ -734,6 +732,22 @@
     }
 
     /**
+     * Draw the text, with origin at (x,y) along the specified path.
+     *
+     * @param textId The text to be drawn
+     * @param path The path the text should follow for its baseline
+     * @param hOffset The distance along the path to add to the text's starting position
+     * @param vOffset The distance above(-) or below(+) the path to position the text
+     */
+    public void addDrawTextOnPath(int textId, Object path, float hOffset, float vOffset) {
+        int pathId = mRemoteComposeState.dataGetId(path);
+        if (pathId == -1) { // never been seen before
+            pathId = addPathData(path);
+        }
+        DrawTextOnPath.apply(mBuffer, textId, pathId, hOffset, vOffset);
+    }
+
+    /**
      * Draw the text, with origin at (x,y). The origin is interpreted based on the Align setting in
      * the paint.
      *
@@ -1646,6 +1660,16 @@
     }
 
     /**
+     * This defines the name of the float given the id
+     *
+     * @param id of the float
+     * @param name name of the float
+     */
+    public void setFloatName(int id, String name) {
+        NamedVariable.apply(mBuffer, id, NamedVariable.FLOAT_TYPE, name);
+    }
+
+    /**
      * Returns a usable component id -- either the one passed in parameter if not -1 or a generated
      * one.
      *
@@ -1685,7 +1709,7 @@
 
     /** Add a component end tag */
     public void addComponentEnd() {
-        ComponentEnd.apply(mBuffer);
+        ContainerEnd.apply(mBuffer);
     }
 
     /**
@@ -1718,7 +1742,7 @@
                 new float[] {notches, notchMax},
                 null);
 
-        OperationsListEnd.apply(mBuffer);
+        ContainerEnd.apply(mBuffer);
     }
 
     /**
@@ -1886,7 +1910,7 @@
     }
 
     public void addLoopEnd() {
-        LoopEnd.apply(mBuffer);
+        ContainerEnd.apply(mBuffer);
     }
 
     public void addStateLayout(
diff --git a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeState.java b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeState.java
index 5c3df7e..cd26198 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeState.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeState.java
@@ -318,7 +318,8 @@
     private void updateListeners(int id) {
         ArrayList<VariableSupport> v = mVarListeners.get(id);
         if (v != null && mRemoteContext != null) {
-            for (VariableSupport c : v) {
+            for (int i = 0; i < v.size(); i++) {
+                VariableSupport c = v.get(i);
                 c.markDirty();
             }
         }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java b/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java
index b5587ce..63469aa 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java
@@ -65,6 +65,8 @@
     public @Nullable Component mLastComponent;
     public long currentTime = 0L;
 
+    private boolean mUseChoreographer = true;
+
     public float getDensity() {
         return mDensity;
     }
@@ -187,6 +189,40 @@
     public abstract void clearNamedIntegerOverride(@NonNull String integerName);
 
     /**
+     * Set the value of a named float. This overrides the float in the document
+     *
+     * @param floatName the name of the float to override
+     * @param value Override the default float
+     */
+    public abstract void setNamedFloatOverride(String floatName, float value);
+
+    /**
+     * Allows to clear a named Float.
+     *
+     * <p>If an override exists, we revert back to the default value in the document.
+     *
+     * @param floatName the name of the float to override
+     */
+    public abstract void clearNamedFloatOverride(String floatName);
+
+    /**
+     * Set the value of a named Object. This overrides the Object in the document
+     *
+     * @param dataName the name of the Object to override
+     * @param value Override the default float
+     */
+    public abstract void setNamedDataOverride(String dataName, Object value);
+
+    /**
+     * Allows to clear a named Object.
+     *
+     * <p>If an override exists, we revert back to the default value in the document.
+     *
+     * @param dataName the name of the Object to override
+     */
+    public abstract void clearNamedDataOverride(String dataName);
+
+    /**
      * Support Collections by registering this collection
      *
      * @param id id of the collection
@@ -224,6 +260,24 @@
     }
 
     /**
+     * Returns true if we should use the choreographter
+     *
+     * @return true if we use the choreographer
+     */
+    public boolean useChoreographer() {
+        return mUseChoreographer;
+    }
+
+    /**
+     * Set to true to use the android choreographer
+     *
+     * @param value
+     */
+    public void setUseChoreographer(boolean value) {
+        mUseChoreographer = value;
+    }
+
+    /**
      * The context can be used in a few different mode, allowing operations to skip being executed:
      * - UNSET : all operations will get executed - DATA : only operations dealing with DATA (eg
      * loading a bitmap) should execute - PAINT : only operations painting should execute
diff --git a/core/java/com/android/internal/widget/remotecompose/core/TimeVariables.java b/core/java/com/android/internal/widget/remotecompose/core/TimeVariables.java
index ea917db..cd5b202 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/TimeVariables.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/TimeVariables.java
@@ -24,7 +24,6 @@
 
 /** This generates the standard system variables for time. */
 public class TimeVariables {
-    private static final float BUILD = 0.02f;
 
     /**
      * This class populates all time variables in the system
@@ -59,7 +58,9 @@
         context.loadFloat(RemoteContext.ID_CALENDAR_MONTH, month);
         context.loadFloat(RemoteContext.ID_DAY_OF_MONTH, month);
         context.loadFloat(RemoteContext.ID_WEEK_DAY, day_week);
-        context.loadFloat(RemoteContext.ID_API_LEVEL, CoreDocument.getDocumentApiLevel() + BUILD);
+        context.loadFloat(
+                RemoteContext.ID_API_LEVEL,
+                CoreDocument.getDocumentApiLevel() + CoreDocument.BUILD);
     }
 
     /**
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextAnchored.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextAnchored.java
index f839922..5d0c437 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextAnchored.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextAnchored.java
@@ -44,8 +44,11 @@
     float mOutPanX;
     float mOutPanY;
 
+    String mLastString;
+
     public static final int ANCHOR_TEXT_RTL = 1;
     public static final int ANCHOR_MONOSPACE_MEASURE = 2;
+    public static final int MEASURE_EVERY_TIME = 4;
 
     public DrawTextAnchored(int textID, float x, float y, float panX, float panY, int flags) {
         mTextID = textID;
@@ -224,7 +227,13 @@
                 ((mFlags & ANCHOR_MONOSPACE_MEASURE) != 0)
                         ? PaintContext.TEXT_MEASURE_MONOSPACE_WIDTH
                         : 0;
-        context.getTextBounds(mTextID, 0, -1, flags, mBounds);
+
+        String str = context.getText(mTextID);
+        if (str != mLastString || (mFlags & MEASURE_EVERY_TIME) != 0) {
+            mLastString = str;
+            context.getTextBounds(mTextID, 0, -1, flags, mBounds);
+        }
+
         float x = mOutX + getHorizontalOffset();
         float y = Float.isNaN(mOutPanY) ? mOutY : mOutY + getVerticalOffset();
         context.drawTextRun(mTextID, 0, -1, 0, 1, x, y, (mFlags & ANCHOR_TEXT_RTL) == 1);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/FloatExpression.java b/core/java/com/android/internal/widget/remotecompose/core/operations/FloatExpression.java
index c1872fd..e09745a 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/FloatExpression.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/FloatExpression.java
@@ -151,21 +151,18 @@
         if (Float.isNaN(mLastChange)) {
             mLastChange = t;
         }
-        float lastComputedValue;
         if (mFloatAnimation != null && !Float.isNaN(mLastCalculatedValue)) {
-            float f = mFloatAnimation.get(t - mLastChange);
-            context.loadFloat(mId, f);
-            lastComputedValue = f;
+            float lastComputedValue = mFloatAnimation.get(t - mLastChange);
             if (lastComputedValue != mLastAnimatedValue) {
                 mLastAnimatedValue = lastComputedValue;
+                context.loadFloat(mId, lastComputedValue);
                 context.needsRepaint();
             }
         } else if (mSpring != null) {
-            float f = mSpring.get(t - mLastChange);
-            context.loadFloat(mId, f);
-            lastComputedValue = f;
+            float lastComputedValue = mSpring.get(t - mLastChange);
             if (lastComputedValue != mLastAnimatedValue) {
                 mLastAnimatedValue = lastComputedValue;
+                context.loadFloat(mId, lastComputedValue);
                 context.needsRepaint();
             }
         } else {
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/Header.java b/core/java/com/android/internal/widget/remotecompose/core/operations/Header.java
index 656dc09..4c96025 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/Header.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/Header.java
@@ -15,11 +15,15 @@
  */
 package com.android.internal.widget.remotecompose.core.operations;
 
+import static com.android.internal.widget.remotecompose.core.CoreDocument.MAJOR_VERSION;
+import static com.android.internal.widget.remotecompose.core.CoreDocument.MINOR_VERSION;
+import static com.android.internal.widget.remotecompose.core.CoreDocument.PATCH_VERSION;
 import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
 import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.LONG;
 
 import android.annotation.NonNull;
 
+import com.android.internal.widget.remotecompose.core.CoreDocument;
 import com.android.internal.widget.remotecompose.core.Operation;
 import com.android.internal.widget.remotecompose.core.Operations;
 import com.android.internal.widget.remotecompose.core.RemoteComposeOperation;
@@ -38,9 +42,6 @@
 public class Header extends Operation implements RemoteComposeOperation {
     private static final int OP_CODE = Operations.HEADER;
     private static final String CLASS_NAME = "Header";
-    public static final int MAJOR_VERSION = 0;
-    public static final int MINOR_VERSION = 2;
-    public static final int PATCH_VERSION = 0;
 
     int mMajorVersion;
     int mMinorVersion;
@@ -191,4 +192,8 @@
                 // .field(FLOAT, "DENSITY", "Major version")
                 .field(LONG, "CAPABILITIES", "Major version");
     }
+
+    public void setVersion(CoreDocument document) {
+        document.setVersion(mMajorVersion, mMinorVersion, mPatchVersion);
+    }
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/TouchExpression.java b/core/java/com/android/internal/widget/remotecompose/core/operations/TouchExpression.java
index 14b72af..3b293bd 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/TouchExpression.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/TouchExpression.java
@@ -97,10 +97,10 @@
     /** Stop at a collection points described in percents of the range */
     public static final int STOP_NOTCHES_PERCENTS = 4;
 
-    /** Stop at a collectiond of point described in abslute cordnates */
+    /** Stop at a collection of point described in absolute cordnates */
     public static final int STOP_NOTCHES_ABSOLUTE = 5;
 
-    /** Jump to the absloute poition of the point */
+    /** Jump to the absolute poition of the point */
     public static final int STOP_ABSOLUTE_POS = 6;
 
     /**
@@ -112,9 +112,9 @@
      * @param min the minimum value
      * @param max the maximum value
      * @param touchEffects the type of touch mode
-     * @param velocityId the valocity (not used)
-     * @param stopMode the behavour on touch oup
-     * @param stopSpec the paraameters that affect the touch up behavour
+     * @param velocityId the velocity (not used)
+     * @param stopMode the behaviour on touch oup
+     * @param stopSpec the parameters that affect the touch up behaviour
      * @param easingSpec the easing parameters for coming to a stop
      */
     public TouchExpression(
@@ -249,10 +249,10 @@
     }
 
     private float getStopPosition(float pos, float slope) {
-        float target = pos + slope / mMaxAcceleration;
+        float target = pos + slope / 2f;
         if (mWrapMode) {
             pos = wrap(pos);
-            target = pos += +slope / mMaxAcceleration;
+            target = pos += +slope / 2f;
         } else {
             target = Math.max(Math.min(target, mOutMax), mOutMin);
         }
@@ -268,7 +268,6 @@
                 int evenSpacing = (int) mOutStopSpec[0];
                 float notchMax = (mOutStopSpec.length > 1) ? mOutStopSpec[1] : mOutMax;
                 float step = (notchMax - min) / evenSpacing;
-
                 float notch = min + step * (int) (0.5f + (target - mOutMin) / step);
                 if (!mWrapMode) {
                     notch = Math.max(Math.min(notch, mOutMax), min);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/Utils.java b/core/java/com/android/internal/widget/remotecompose/core/operations/Utils.java
index de43b90..bd68d5a 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/Utils.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/Utils.java
@@ -41,6 +41,16 @@
     }
 
     /**
+     * Converts an id encoded in a float to the corresponding long id.
+     *
+     * @param value the float if to convert
+     * @return the float id converted to a long id
+     */
+    public static long longIdFromNan(float value) {
+        return ((long) idFromNan(value)) + 0x100000000L;
+    }
+
+    /**
      * convert a long into an ID
      *
      * @param v the long to convert
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/AnimatableValue.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/AnimatableValue.java
index 652ab2b..1432233 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/AnimatableValue.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/AnimatableValue.java
@@ -26,11 +26,13 @@
     int mId = 0;
     float mValue = 0f;
 
+    boolean mAnimateValueChanges = true;
     boolean mAnimate = false;
     long mAnimateTargetTime = 0;
     float mAnimateDuration = 300f;
     float mTargetRotationX;
     float mStartRotationX;
+    long mLastUpdate = 0L;
 
     int mMotionEasingType = GeneralEasing.CUBIC_STANDARD;
     FloatAnimation mMotionEasing;
@@ -39,8 +41,10 @@
      * Value to animate
      *
      * @param value value
+     * @param animateValueChanges animate the change of values
      */
-    public AnimatableValue(float value) {
+    public AnimatableValue(float value, boolean animateValueChanges) {
+        mAnimateValueChanges = animateValueChanges;
         if (Utils.isVariable(value)) {
             mId = Utils.idFromNan(value);
             mIsVariable = true;
@@ -50,6 +54,15 @@
     }
 
     /**
+     * Value to animate.
+     *
+     * @param value value
+     */
+    public AnimatableValue(float value) {
+        this(value, true);
+    }
+
+    /**
      * Get the value
      *
      * @return the value
@@ -69,29 +82,41 @@
             return mValue;
         }
         float value = context.getContext().mRemoteComposeState.getFloat(mId);
-
-        if (value != mValue && !mAnimate) {
-            // animate
-            mStartRotationX = mValue;
-            mTargetRotationX = value;
-            mAnimate = true;
-            mAnimateTargetTime = System.currentTimeMillis();
-            mMotionEasing =
-                    new FloatAnimation(
-                            mMotionEasingType, mAnimateDuration / 1000f, null, 0f, Float.NaN);
-            mMotionEasing.setTargetValue(1f);
-        }
-        if (mAnimate) {
-            float elapsed = System.currentTimeMillis() - mAnimateTargetTime;
-            float p = mMotionEasing.get(elapsed / mAnimateDuration);
-            mValue = (1 - p) * mStartRotationX + p * mTargetRotationX;
-            if (p >= 1f) {
-                mAnimate = false;
+        if (value != mValue) {
+            long lastUpdate = System.currentTimeMillis();
+            long interval = lastUpdate - mLastUpdate;
+            if (interval > mAnimateDuration && mLastUpdate != 0L) {
+                mAnimateValueChanges = true;
+            } else {
+                mAnimateValueChanges = false;
             }
-        } else {
-            mValue = mTargetRotationX;
+            mLastUpdate = lastUpdate;
         }
-
+        if (!mAnimateValueChanges) {
+            mValue = value;
+        } else {
+            if (value != mValue && !mAnimate) {
+                // animate
+                mStartRotationX = mValue;
+                mTargetRotationX = value;
+                mAnimate = true;
+                mAnimateTargetTime = System.currentTimeMillis();
+                mMotionEasing =
+                        new FloatAnimation(
+                                mMotionEasingType, mAnimateDuration / 1000f, null, 0f, Float.NaN);
+                mMotionEasing.setTargetValue(1f);
+            }
+            if (mAnimate) {
+                float elapsed = System.currentTimeMillis() - mAnimateTargetTime;
+                float p = mMotionEasing.get(elapsed / mAnimateDuration);
+                mValue = (1 - p) * mStartRotationX + p * mTargetRotationX;
+                if (p >= 1f) {
+                    mAnimate = false;
+                }
+            } else {
+                mValue = mTargetRotationX;
+            }
+        }
         return mValue;
     }
 
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/CanvasContent.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/CanvasContent.java
index 34b7a23..511858aa 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/CanvasContent.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/CanvasContent.java
@@ -28,7 +28,7 @@
 import java.util.List;
 
 /** Represents the content of a CanvasLayout (i.e. contains the canvas commands) */
-public class CanvasContent extends Component implements ComponentStartOperation {
+public class CanvasContent extends Component {
 
     public CanvasContent(
             int componentId,
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ClickModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ClickModifierOperation.java
index e05bdf2..5f084e9 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ClickModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ClickModifierOperation.java
@@ -42,7 +42,11 @@
 
 /** Represents a click modifier + actions */
 public class ClickModifierOperation extends PaintOperation
-        implements ModifierOperation, DecoratorComponent, ClickHandler, AccessibleComponent {
+        implements Container,
+                ModifierOperation,
+                DecoratorComponent,
+                ClickHandler,
+                AccessibleComponent {
     private static final int OP_CODE = Operations.MODIFIER_CLICK;
 
     long mAnimateRippleStart = 0;
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/Component.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/Component.java
index 8a77dc3..eee2aab 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/Component.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/Component.java
@@ -41,7 +41,8 @@
 import java.util.HashSet;
 
 /** Generic Component class */
-public class Component extends PaintOperation implements Measurable, SerializableToString {
+public class Component extends PaintOperation
+        implements Container, Measurable, SerializableToString {
 
     private static final boolean DEBUG = false;
 
@@ -68,9 +69,7 @@
 
     private boolean mNeedsBoundsAnimation = false;
 
-    /**
-     * Mark the component as needing a bounds animation pass
-     */
+    /** Mark the component as needing a bounds animation pass */
     public void markNeedsBoundsAnimation() {
         mNeedsBoundsAnimation = true;
         if (mParent != null && !mParent.mNeedsBoundsAnimation) {
@@ -78,9 +77,7 @@
         }
     }
 
-    /**
-     * Clear the bounds animation pass flag
-     */
+    /** Clear the bounds animation pass flag */
     public void clearNeedsBoundsAnimation() {
         mNeedsBoundsAnimation = false;
     }
@@ -426,13 +423,13 @@
 
     /**
      * Animate the bounds of the component as needed
+     *
      * @param context
      */
     public void animatingBounds(@NonNull RemoteContext context) {
         if (mAnimateMeasure != null) {
             mAnimateMeasure.apply(context);
             updateComponentValues(context);
-            markNeedsBoundsAnimation();
         } else {
             clearNeedsBoundsAnimation();
         }
@@ -784,7 +781,13 @@
     public boolean applyAnimationAsNeeded(@NonNull PaintContext context) {
         if (context.isAnimationEnabled() && mAnimateMeasure != null) {
             mAnimateMeasure.paint(context);
-            context.needsRepaint();
+            if (mAnimateMeasure.isDone()) {
+                mAnimateMeasure = null;
+                clearNeedsBoundsAnimation();
+                needsRepaint();
+            } else {
+                markNeedsBoundsAnimation();
+            }
             return true;
         }
         return false;
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ComponentEnd.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ComponentEnd.java
deleted file mode 100644
index 5da0663..0000000
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ComponentEnd.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.internal.widget.remotecompose.core.operations.layout;
-
-import android.annotation.NonNull;
-
-import com.android.internal.widget.remotecompose.core.Operation;
-import com.android.internal.widget.remotecompose.core.Operations;
-import com.android.internal.widget.remotecompose.core.RemoteContext;
-import com.android.internal.widget.remotecompose.core.WireBuffer;
-import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
-
-import java.util.List;
-
-public class ComponentEnd extends Operation {
-
-    @Override
-    public void write(@NonNull WireBuffer buffer) {
-        apply(buffer);
-    }
-
-    @NonNull
-    @Override
-    public String toString() {
-        return "COMPONENT_END";
-    }
-
-    @Override
-    public void apply(@NonNull RemoteContext context) {
-        // nothing
-    }
-
-    @NonNull
-    @Override
-    public String deepToString(@NonNull String indent) {
-        return (indent != null ? indent : "") + toString();
-    }
-
-    /**
-     * The name of the class
-     *
-     * @return the name
-     */
-    @NonNull
-    public static String name() {
-        return "ComponentEnd";
-    }
-
-    /**
-     * The OP_CODE for this command
-     *
-     * @return the opcode
-     */
-    public static int id() {
-        return Operations.COMPONENT_END;
-    }
-
-    public static void apply(@NonNull WireBuffer buffer) {
-        buffer.start(Operations.COMPONENT_END);
-    }
-
-    public static int size() {
-        return 1 + 4 + 4 + 4;
-    }
-
-    /**
-     * Read this operation and add it to the list of operations
-     *
-     * @param buffer the buffer to read
-     * @param operations the list of operations that will be added to
-     */
-    public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
-        operations.add(new ComponentEnd());
-    }
-
-    /**
-     * Populate the documentation with a description of this operation
-     *
-     * @param doc to append the description to.
-     */
-    public static void documentation(@NonNull DocumentationBuilder doc) {
-        doc.operation("Layout Operations", id(), name())
-                .description(
-                        "End tag for components / layouts. This operation marks the end"
-                                + "of a component");
-    }
-}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ComponentStart.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ComponentStart.java
index 4349b31..f009d88 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ComponentStart.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ComponentStart.java
@@ -26,9 +26,10 @@
 import com.android.internal.widget.remotecompose.core.WireBuffer;
 import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
 
+import java.util.ArrayList;
 import java.util.List;
 
-public class ComponentStart extends Operation implements ComponentStartOperation {
+public class ComponentStart extends Operation implements Container {
 
     int mType = DEFAULT;
     float mX;
@@ -37,6 +38,8 @@
     float mHeight;
     int mComponentId;
 
+    @NonNull public ArrayList<Operation> mList = new ArrayList<>();
+
     public int getType() {
         return mType;
     }
@@ -217,4 +220,10 @@
                 .field(FLOAT, "WIDTH", "width of the component")
                 .field(FLOAT, "HEIGHT", "height of the component");
     }
+
+    @NonNull
+    @Override
+    public ArrayList<Operation> getList() {
+        return mList;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.kt b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/Container.java
similarity index 62%
copy from packages/SystemUI/src/com/android/keyguard/LockIconViewController.kt
copy to core/java/com/android/internal/widget/remotecompose/core/operations/layout/Container.java
index c5012b0..c678f6c 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.kt
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/Container.java
@@ -13,23 +13,15 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package com.android.internal.widget.remotecompose.core.operations.layout;
 
-package com.android.keyguard
+import android.annotation.NonNull;
 
-import android.view.MotionEvent
-import android.view.View
+import com.android.internal.widget.remotecompose.core.Operation;
 
-/** Controls the [LockIconView]. */
-interface LockIconViewController {
-    fun setLockIconView(lockIconView: View)
+import java.util.ArrayList;
 
-    fun getTop(): Float
-
-    fun getBottom(): Float
-
-    fun dozeTimeTick()
-
-    fun setAlpha(alpha: Float)
-
-    fun willHandleTouchWhileDozing(event: MotionEvent): Boolean
+public interface Container {
+    @NonNull
+    ArrayList<Operation> getList();
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/OperationsListEnd.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ContainerEnd.java
similarity index 94%
rename from core/java/com/android/internal/widget/remotecompose/core/operations/layout/OperationsListEnd.java
rename to core/java/com/android/internal/widget/remotecompose/core/operations/layout/ContainerEnd.java
index 12a673d..4290c4b 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/OperationsListEnd.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ContainerEnd.java
@@ -25,7 +25,7 @@
 
 import java.util.List;
 
-public class OperationsListEnd extends Operation {
+public class ContainerEnd extends Operation {
 
     @Override
     public void write(@NonNull WireBuffer buffer) {
@@ -65,7 +65,7 @@
      * @return the opcode
      */
     public static int id() {
-        return Operations.OPERATIONS_LIST_END;
+        return Operations.CONTAINER_END;
     }
 
     public static void apply(@NonNull WireBuffer buffer) {
@@ -79,7 +79,7 @@
      * @param operations the list of operations that will be added to
      */
     public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
-        operations.add(new OperationsListEnd());
+        operations.add(new ContainerEnd());
     }
 
     /**
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponentContent.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponentContent.java
index 9bfbe6a..27172aa 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponentContent.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponentContent.java
@@ -28,7 +28,7 @@
 import java.util.List;
 
 /** Represents the content of a LayoutComponent (i.e. the children components) */
-public class LayoutComponentContent extends Component implements ComponentStartOperation {
+public class LayoutComponentContent extends Component {
 
     public LayoutComponentContent(
             int componentId,
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ListActionsOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ListActionsOperation.java
index 9fc5da8..6dce6f1 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ListActionsOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ListActionsOperation.java
@@ -29,7 +29,7 @@
 import java.util.ArrayList;
 
 public abstract class ListActionsOperation extends PaintOperation
-        implements ModifierOperation, DecoratorComponent {
+        implements Container, ModifierOperation, DecoratorComponent {
 
     String mOperationName;
     protected float mWidth = 0;
@@ -43,6 +43,7 @@
 
     public ArrayList<Operation> mList = new ArrayList<>();
 
+    @NonNull
     public ArrayList<Operation> getList() {
         return mList;
     }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LoopEnd.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LoopEnd.java
deleted file mode 100644
index 3d389e5..0000000
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LoopEnd.java
+++ /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.internal.widget.remotecompose.core.operations.layout;
-
-import android.annotation.NonNull;
-
-import com.android.internal.widget.remotecompose.core.Operation;
-import com.android.internal.widget.remotecompose.core.Operations;
-import com.android.internal.widget.remotecompose.core.RemoteContext;
-import com.android.internal.widget.remotecompose.core.WireBuffer;
-import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
-
-import java.util.List;
-
-public class LoopEnd extends Operation {
-
-    @Override
-    public void write(@NonNull WireBuffer buffer) {
-        apply(buffer);
-    }
-
-    @NonNull
-    @Override
-    public String toString() {
-        return "LOOP_END";
-    }
-
-    @Override
-    public void apply(@NonNull RemoteContext context) {
-        // nothing
-    }
-
-    @NonNull
-    @Override
-    public String deepToString(@NonNull String indent) {
-        return (indent != null ? indent : "") + toString();
-    }
-
-    /**
-     * The name of the class
-     *
-     * @return the name
-     */
-    @NonNull
-    public static String name() {
-        return "LoopEnd";
-    }
-
-    /**
-     * The OP_CODE for this command
-     *
-     * @return the opcode
-     */
-    public static int id() {
-        return Operations.LOOP_END;
-    }
-
-    public static void apply(@NonNull WireBuffer buffer) {
-        buffer.start(id());
-    }
-
-    /**
-     * Read this operation and add it to the list of operations
-     *
-     * @param buffer the buffer to read
-     * @param operations the list of operations that will be added to
-     */
-    public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
-        operations.add(new LoopEnd());
-    }
-
-    /**
-     * Populate the documentation with a description of this operation
-     *
-     * @param doc to append the description to.
-     */
-    public static void documentation(@NonNull DocumentationBuilder doc) {
-        doc.operation("Operations", id(), name()).description("End tag for loops");
-    }
-}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LoopOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LoopOperation.java
index ab1e0ac..f5954ee 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LoopOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LoopOperation.java
@@ -32,7 +32,7 @@
 import java.util.List;
 
 /** Represents a loop of operations */
-public class LoopOperation extends PaintOperation implements VariableSupport {
+public class LoopOperation extends PaintOperation implements Container, VariableSupport {
     private static final int OP_CODE = Operations.LOOP_START;
 
     @NonNull public ArrayList<Operation> mList = new ArrayList<>();
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/RootLayoutComponent.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/RootLayoutComponent.java
index 11fa7ee..baff5ee 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/RootLayoutComponent.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/RootLayoutComponent.java
@@ -36,7 +36,7 @@
 import java.util.List;
 
 /** Represents the root layout component. Entry point to the component tree layout/paint. */
-public class RootLayoutComponent extends Component implements ComponentStartOperation {
+public class RootLayoutComponent extends Component {
     private int mCurrentId = -1;
     private boolean mHasTouchListeners = false;
 
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/AnimateMeasure.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/AnimateMeasure.java
index 1de956b..d3b3e0e 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/AnimateMeasure.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/AnimateMeasure.java
@@ -323,7 +323,6 @@
         }
 
         if (mP >= 1f && mVp >= 1f) {
-            mComponent.mAnimateMeasure = null;
             mComponent.mVisibility = mTarget.getVisibility();
         }
     }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/BoxLayout.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/BoxLayout.java
index 8076cb1..a37f35f 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/BoxLayout.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/BoxLayout.java
@@ -26,7 +26,6 @@
 import com.android.internal.widget.remotecompose.core.WireBuffer;
 import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
 import com.android.internal.widget.remotecompose.core.operations.layout.Component;
-import com.android.internal.widget.remotecompose.core.operations.layout.ComponentStartOperation;
 import com.android.internal.widget.remotecompose.core.operations.layout.measure.ComponentMeasure;
 import com.android.internal.widget.remotecompose.core.operations.layout.measure.MeasurePass;
 import com.android.internal.widget.remotecompose.core.operations.layout.measure.Size;
@@ -34,7 +33,7 @@
 import java.util.List;
 
 /** Simple Box layout implementation */
-public class BoxLayout extends LayoutManager implements ComponentStartOperation {
+public class BoxLayout extends LayoutManager {
 
     public static final int START = 1;
     public static final int CENTER = 2;
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/ColumnLayout.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/ColumnLayout.java
index 249e84a..f68d7b4 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/ColumnLayout.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/ColumnLayout.java
@@ -28,7 +28,6 @@
 import com.android.internal.widget.remotecompose.core.WireBuffer;
 import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
 import com.android.internal.widget.remotecompose.core.operations.layout.Component;
-import com.android.internal.widget.remotecompose.core.operations.layout.ComponentStartOperation;
 import com.android.internal.widget.remotecompose.core.operations.layout.LayoutComponent;
 import com.android.internal.widget.remotecompose.core.operations.layout.measure.ComponentMeasure;
 import com.android.internal.widget.remotecompose.core.operations.layout.measure.MeasurePass;
@@ -40,7 +39,7 @@
 /**
  * Simple Column layout implementation - also supports weight and horizontal/vertical positioning
  */
-public class ColumnLayout extends LayoutManager implements ComponentStartOperation {
+public class ColumnLayout extends LayoutManager {
     public static final int START = 1;
     public static final int CENTER = 2;
     public static final int END = 3;
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/LayoutManager.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/LayoutManager.java
index a5edaa8..edfd69c 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/LayoutManager.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/LayoutManager.java
@@ -135,33 +135,26 @@
         float measuredWidth = Math.min(maxWidth, computeModifierDefinedWidth(context.getContext()));
         float measuredHeight =
                 Math.min(maxHeight, computeModifierDefinedHeight(context.getContext()));
-        float insetMaxWidth = maxWidth - mPaddingLeft - mPaddingRight;
-        float insetMaxHeight = maxHeight - mPaddingTop - mPaddingBottom;
 
         if (mWidthModifier.isIntrinsicMin()) {
-            maxWidth = intrinsicWidth(context.getContext());
+            maxWidth = intrinsicWidth(context.getContext()) + mPaddingLeft + mPaddingRight;
         }
         if (mHeightModifier.isIntrinsicMin()) {
-            maxHeight = intrinsicHeight(context.getContext());
+            maxHeight = intrinsicHeight(context.getContext()) + mPaddingTop + mPaddingBottom;
         }
 
+        float insetMaxWidth = maxWidth - mPaddingLeft - mPaddingRight;
+        float insetMaxHeight = maxHeight - mPaddingTop - mPaddingBottom;
+
         boolean hasHorizontalWrap = mWidthModifier.isWrap();
         boolean hasVerticalWrap = mHeightModifier.isWrap();
         if (hasHorizontalWrap || hasVerticalWrap) { // TODO: potential npe -- bbade@
             mCachedWrapSize.setWidth(0f);
             mCachedWrapSize.setHeight(0f);
-            float wrapMaxWidth = insetMaxWidth;
-            float wrapMaxHeight = insetMaxHeight;
-            if (hasHorizontalWrap) {
-                wrapMaxWidth = insetMaxWidth - mPaddingLeft - mPaddingRight;
-            }
-            if (hasVerticalWrap) {
-                wrapMaxHeight = insetMaxHeight - mPaddingTop - mPaddingBottom;
-            }
             computeWrapSize(
                     context,
-                    wrapMaxWidth,
-                    wrapMaxHeight,
+                    insetMaxWidth,
+                    insetMaxHeight,
                     mWidthModifier.isWrap(),
                     mHeightModifier.isWrap(),
                     measure,
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/RowLayout.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/RowLayout.java
index 37b9a68..b688f6e 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/RowLayout.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/RowLayout.java
@@ -28,7 +28,6 @@
 import com.android.internal.widget.remotecompose.core.WireBuffer;
 import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
 import com.android.internal.widget.remotecompose.core.operations.layout.Component;
-import com.android.internal.widget.remotecompose.core.operations.layout.ComponentStartOperation;
 import com.android.internal.widget.remotecompose.core.operations.layout.LayoutComponent;
 import com.android.internal.widget.remotecompose.core.operations.layout.measure.ComponentMeasure;
 import com.android.internal.widget.remotecompose.core.operations.layout.measure.MeasurePass;
@@ -38,7 +37,7 @@
 import java.util.List;
 
 /** Simple Row layout implementation - also supports weight and horizontal/vertical positioning */
-public class RowLayout extends LayoutManager implements ComponentStartOperation {
+public class RowLayout extends LayoutManager {
     public static final int START = 1;
     public static final int CENTER = 2;
     public static final int END = 3;
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/StateLayout.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/StateLayout.java
index 61a3ec9..3044797 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/StateLayout.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/StateLayout.java
@@ -26,7 +26,6 @@
 import com.android.internal.widget.remotecompose.core.RemoteContext;
 import com.android.internal.widget.remotecompose.core.WireBuffer;
 import com.android.internal.widget.remotecompose.core.operations.layout.Component;
-import com.android.internal.widget.remotecompose.core.operations.layout.ComponentStartOperation;
 import com.android.internal.widget.remotecompose.core.operations.layout.LayoutComponent;
 import com.android.internal.widget.remotecompose.core.operations.layout.measure.ComponentMeasure;
 import com.android.internal.widget.remotecompose.core.operations.layout.measure.MeasurePass;
@@ -43,7 +42,7 @@
  * <p>States are defined as child layouts. This layout handles interpolating between the different
  * state in order to provide an automatic transition.
  */
-public class StateLayout extends LayoutManager implements ComponentStartOperation {
+public class StateLayout extends LayoutManager {
 
     public int measuredLayoutIndex = 0;
     public int currentLayoutIndex = 0;
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/TextLayout.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/TextLayout.java
index 910205e..8157ea0 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/TextLayout.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/TextLayout.java
@@ -29,7 +29,6 @@
 import com.android.internal.widget.remotecompose.core.WireBuffer;
 import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
 import com.android.internal.widget.remotecompose.core.operations.layout.Component;
-import com.android.internal.widget.remotecompose.core.operations.layout.ComponentStartOperation;
 import com.android.internal.widget.remotecompose.core.operations.layout.measure.MeasurePass;
 import com.android.internal.widget.remotecompose.core.operations.layout.measure.Size;
 import com.android.internal.widget.remotecompose.core.operations.paint.PaintBundle;
@@ -39,8 +38,7 @@
 import java.util.List;
 
 /** Text component, referencing a text id */
-public class TextLayout extends LayoutManager
-        implements ComponentStartOperation, VariableSupport, AccessibleComponent {
+public class TextLayout extends LayoutManager implements VariableSupport, AccessibleComponent {
 
     private static final boolean DEBUG = false;
     private int mTextId = -1;
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ScrollModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ScrollModifierOperation.java
index a5f79ee..8950579 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ScrollModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ScrollModifierOperation.java
@@ -31,6 +31,7 @@
 import com.android.internal.widget.remotecompose.core.operations.Utils;
 import com.android.internal.widget.remotecompose.core.operations.layout.Component;
 import com.android.internal.widget.remotecompose.core.operations.layout.DecoratorComponent;
+import com.android.internal.widget.remotecompose.core.operations.layout.LayoutComponent;
 import com.android.internal.widget.remotecompose.core.operations.layout.ListActionsOperation;
 import com.android.internal.widget.remotecompose.core.operations.layout.RootLayoutComponent;
 import com.android.internal.widget.remotecompose.core.operations.layout.ScrollDelegate;
@@ -212,17 +213,38 @@
                 .field(INT, "direction", "");
     }
 
+    private float getMaxScrollPosition(Component component, int direction) {
+        if (component instanceof LayoutComponent) {
+            LayoutComponent layoutComponent = (LayoutComponent) component;
+            int numChildren = layoutComponent.getChildrenComponents().size();
+            if (numChildren > 0) {
+                Component lastChild = layoutComponent.getChildrenComponents().get(numChildren - 1);
+                if (direction == 0) { // VERTICAL
+                    return lastChild.getY();
+                } else {
+                    return lastChild.getX();
+                }
+            }
+        }
+        return 0f;
+    }
+
     @Override
     public void layout(RemoteContext context, Component component, float width, float height) {
         mWidth = width;
         mHeight = height;
-        if (mDirection == 0) { // VERTICAL
-            context.loadFloat(Utils.idFromNan(mMax), mMaxScrollY);
-            context.loadFloat(Utils.idFromNan(mNotchMax), mContentDimension);
-        } else {
-            context.loadFloat(Utils.idFromNan(mMax), mMaxScrollX);
-            context.loadFloat(Utils.idFromNan(mNotchMax), mContentDimension);
+        float max = mMaxScrollY;
+        if (mDirection != 0) { // HORIZONTAL
+            max = mMaxScrollX;
         }
+        if (mTouchExpression != null) {
+            float maxScrollPosition = getMaxScrollPosition(component, mDirection);
+            if (maxScrollPosition > 0) {
+                max = maxScrollPosition;
+            }
+        }
+        context.loadFloat(Utils.idFromNan(mMax), max);
+        context.loadFloat(Utils.idFromNan(mNotchMax), mContentDimension);
     }
 
     @Override
diff --git a/core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java b/core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java
index 6eb83f1..5de11a19 100644
--- a/core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java
+++ b/core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java
@@ -15,6 +15,9 @@
  */
 package com.android.internal.widget.remotecompose.player;
 
+import static com.android.internal.widget.remotecompose.core.CoreDocument.MAJOR_VERSION;
+import static com.android.internal.widget.remotecompose.core.CoreDocument.MINOR_VERSION;
+
 import android.app.Application;
 import android.content.Context;
 import android.content.res.TypedArray;
@@ -32,6 +35,7 @@
 import android.widget.HorizontalScrollView;
 import android.widget.ScrollView;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.widget.remotecompose.accessibility.RemoteComposeTouchHelper;
 import com.android.internal.widget.remotecompose.core.CoreDocument;
 import com.android.internal.widget.remotecompose.core.RemoteContext;
@@ -43,8 +47,8 @@
 public class RemoteComposePlayer extends FrameLayout {
     private RemoteComposeCanvas mInner;
 
-    private static final int MAX_SUPPORTED_MAJOR_VERSION = 0;
-    private static final int MAX_SUPPORTED_MINOR_VERSION = 1;
+    private static final int MAX_SUPPORTED_MAJOR_VERSION = MAJOR_VERSION;
+    private static final int MAX_SUPPORTED_MINOR_VERSION = MINOR_VERSION;
 
     public RemoteComposePlayer(Context context) {
         super(context);
@@ -259,6 +263,16 @@
         return mInner.getDocument().mDocument.getOpsPerFrame();
     }
 
+    /**
+     * Set to use the choreographer
+     *
+     * @param value
+     */
+    @VisibleForTesting
+    public void setUseChoreographer(boolean value) {
+        mInner.setUseChoreographer(value);
+    }
+
     /** Id action callback interface */
     public interface IdActionCallbacks {
         /**
diff --git a/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidPaintContext.java b/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidPaintContext.java
index 0712ea4..16e0e05 100644
--- a/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidPaintContext.java
+++ b/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidPaintContext.java
@@ -16,6 +16,7 @@
 package com.android.internal.widget.remotecompose.player.platform;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.graphics.Bitmap;
 import android.graphics.BlendMode;
 import android.graphics.Canvas;
@@ -247,6 +248,8 @@
         mCanvas.drawTextOnPath(getText(textId), getPath(pathId, 0, 1), hOffset, vOffset, mPaint);
     }
 
+    private Paint.FontMetrics mCachedFontMetrics;
+
     @Override
     public void getTextBounds(int textId, int start, int end, int flags, @NonNull float[] bounds) {
         String str = getText(textId);
@@ -254,7 +257,10 @@
             end = str.length();
         }
 
-        Paint.FontMetrics metrics = mPaint.getFontMetrics();
+        if (mCachedFontMetrics == null) {
+            mCachedFontMetrics = mPaint.getFontMetrics();
+        }
+        mPaint.getFontMetrics(mCachedFontMetrics);
         mPaint.getTextBounds(str, start, end, mTmpRect);
 
         bounds[0] = mTmpRect.left;
@@ -266,8 +272,8 @@
         }
 
         if ((flags & PaintContext.TEXT_MEASURE_FONT_HEIGHT) != 0) {
-            bounds[1] = Math.round(metrics.ascent);
-            bounds[3] = Math.round(metrics.bottom);
+            bounds[1] = Math.round(mCachedFontMetrics.ascent);
+            bounds[3] = Math.round(mCachedFontMetrics.bottom);
         } else {
             bounds[1] = mTmpRect.top;
             bounds[3] = mTmpRect.bottom;
@@ -415,6 +421,210 @@
         return null;
     }
 
+    PaintChanges mCachedPaintChanges =
+            new PaintChanges() {
+                @Override
+                public void setTextSize(float size) {
+                    mPaint.setTextSize(size);
+                }
+
+                @Override
+                public void setTypeFace(int fontType, int weight, boolean italic) {
+                    int[] type =
+                            new int[] {
+                                Typeface.NORMAL,
+                                Typeface.BOLD,
+                                Typeface.ITALIC,
+                                Typeface.BOLD_ITALIC
+                            };
+
+                    switch (fontType) {
+                        case PaintBundle.FONT_TYPE_DEFAULT:
+                            if (weight == 400 && !italic) { // for normal case
+                                mPaint.setTypeface(Typeface.DEFAULT);
+                            } else {
+                                mPaint.setTypeface(
+                                        Typeface.create(Typeface.DEFAULT, weight, italic));
+                            }
+                            break;
+                        case PaintBundle.FONT_TYPE_SERIF:
+                            if (weight == 400 && !italic) { // for normal case
+                                mPaint.setTypeface(Typeface.SERIF);
+                            } else {
+                                mPaint.setTypeface(Typeface.create(Typeface.SERIF, weight, italic));
+                            }
+                            break;
+                        case PaintBundle.FONT_TYPE_SANS_SERIF:
+                            if (weight == 400 && !italic) { //  for normal case
+                                mPaint.setTypeface(Typeface.SANS_SERIF);
+                            } else {
+                                mPaint.setTypeface(
+                                        Typeface.create(Typeface.SANS_SERIF, weight, italic));
+                            }
+                            break;
+                        case PaintBundle.FONT_TYPE_MONOSPACE:
+                            if (weight == 400 && !italic) { //  for normal case
+                                mPaint.setTypeface(Typeface.MONOSPACE);
+                            } else {
+                                mPaint.setTypeface(
+                                        Typeface.create(Typeface.MONOSPACE, weight, italic));
+                            }
+
+                            break;
+                    }
+                }
+
+                @Override
+                public void setStrokeWidth(float width) {
+                    mPaint.setStrokeWidth(width);
+                }
+
+                @Override
+                public void setColor(int color) {
+                    mPaint.setColor(color);
+                }
+
+                @Override
+                public void setStrokeCap(int cap) {
+                    mPaint.setStrokeCap(Paint.Cap.values()[cap]);
+                }
+
+                @Override
+                public void setStyle(int style) {
+                    mPaint.setStyle(Paint.Style.values()[style]);
+                }
+
+                @Override
+                public void setShader(int shaderId) {
+                    // TODO this stuff should check the shader creation
+                    if (shaderId == 0) {
+                        mPaint.setShader(null);
+                        return;
+                    }
+                    ShaderData data = getShaderData(shaderId);
+                    if (data == null) {
+                        return;
+                    }
+                    RuntimeShader shader = new RuntimeShader(getText(data.getShaderTextId()));
+                    String[] names = data.getUniformFloatNames();
+                    for (int i = 0; i < names.length; i++) {
+                        String name = names[i];
+                        float[] val = data.getUniformFloats(name);
+                        shader.setFloatUniform(name, val);
+                    }
+                    names = data.getUniformIntegerNames();
+                    for (int i = 0; i < names.length; i++) {
+                        String name = names[i];
+                        int[] val = data.getUniformInts(name);
+                        shader.setIntUniform(name, val);
+                    }
+                    names = data.getUniformBitmapNames();
+                    for (int i = 0; i < names.length; i++) {
+                        String name = names[i];
+                        int val = data.getUniformBitmapId(name);
+                    }
+                    mPaint.setShader(shader);
+                }
+
+                @Override
+                public void setImageFilterQuality(int quality) {
+                    Utils.log(" quality =" + quality);
+                    mPaint.setFilterBitmap(quality == 1);
+                }
+
+                @Override
+                public void setBlendMode(int mode) {
+                    mPaint.setBlendMode(origamiToBlendMode(mode));
+                }
+
+                @Override
+                public void setAlpha(float a) {
+                    mPaint.setAlpha((int) (255 * a));
+                }
+
+                @Override
+                public void setStrokeMiter(float miter) {
+                    mPaint.setStrokeMiter(miter);
+                }
+
+                @Override
+                public void setStrokeJoin(int join) {
+                    mPaint.setStrokeJoin(Paint.Join.values()[join]);
+                }
+
+                @Override
+                public void setFilterBitmap(boolean filter) {
+                    mPaint.setFilterBitmap(filter);
+                }
+
+                @Override
+                public void setAntiAlias(boolean aa) {
+                    mPaint.setAntiAlias(aa);
+                }
+
+                @Override
+                public void clear(long mask) {
+                    if ((mask & (1L << PaintBundle.COLOR_FILTER)) != 0) {
+                        mPaint.setColorFilter(null);
+                    }
+                }
+
+                Shader.TileMode[] mTileModes =
+                        new Shader.TileMode[] {
+                            Shader.TileMode.CLAMP, Shader.TileMode.REPEAT, Shader.TileMode.MIRROR
+                        };
+
+                @Override
+                public void setLinearGradient(
+                        @NonNull int[] colors,
+                        @NonNull float[] stops,
+                        float startX,
+                        float startY,
+                        float endX,
+                        float endY,
+                        int tileMode) {
+                    mPaint.setShader(
+                            new LinearGradient(
+                                    startX,
+                                    startY,
+                                    endX,
+                                    endY,
+                                    colors,
+                                    stops,
+                                    mTileModes[tileMode]));
+                }
+
+                @Override
+                public void setRadialGradient(
+                        @NonNull int[] colors,
+                        @NonNull float[] stops,
+                        float centerX,
+                        float centerY,
+                        float radius,
+                        int tileMode) {
+                    mPaint.setShader(
+                            new RadialGradient(
+                                    centerX, centerY, radius, colors, stops, mTileModes[tileMode]));
+                }
+
+                @Override
+                public void setSweepGradient(
+                        @NonNull int[] colors,
+                        @NonNull float[] stops,
+                        float centerX,
+                        float centerY) {
+                    mPaint.setShader(new SweepGradient(centerX, centerY, colors, stops));
+                }
+
+                @Override
+                public void setColorFilter(int color, int mode) {
+                    PorterDuff.Mode pmode = origamiToPorterDuffMode(mode);
+                    if (pmode != null) {
+                        mPaint.setColorFilter(new PorterDuffColorFilter(color, pmode));
+                    }
+                }
+            };
+
     /**
      * This applies paint changes to the current paint
      *
@@ -422,218 +632,7 @@
      */
     @Override
     public void applyPaint(@NonNull PaintBundle paintData) {
-        paintData.applyPaintChange(
-                (PaintContext) this,
-                new PaintChanges() {
-                    @Override
-                    public void setTextSize(float size) {
-                        mPaint.setTextSize(size);
-                    }
-
-                    @Override
-                    public void setTypeFace(int fontType, int weight, boolean italic) {
-                        int[] type =
-                                new int[] {
-                                    Typeface.NORMAL,
-                                    Typeface.BOLD,
-                                    Typeface.ITALIC,
-                                    Typeface.BOLD_ITALIC
-                                };
-
-                        switch (fontType) {
-                            case PaintBundle.FONT_TYPE_DEFAULT:
-                                if (weight == 400 && !italic) { // for normal case
-                                    mPaint.setTypeface(Typeface.DEFAULT);
-                                } else {
-                                    mPaint.setTypeface(
-                                            Typeface.create(Typeface.DEFAULT, weight, italic));
-                                }
-                                break;
-                            case PaintBundle.FONT_TYPE_SERIF:
-                                if (weight == 400 && !italic) { // for normal case
-                                    mPaint.setTypeface(Typeface.SERIF);
-                                } else {
-                                    mPaint.setTypeface(
-                                            Typeface.create(Typeface.SERIF, weight, italic));
-                                }
-                                break;
-                            case PaintBundle.FONT_TYPE_SANS_SERIF:
-                                if (weight == 400 && !italic) { //  for normal case
-                                    mPaint.setTypeface(Typeface.SANS_SERIF);
-                                } else {
-                                    mPaint.setTypeface(
-                                            Typeface.create(Typeface.SANS_SERIF, weight, italic));
-                                }
-                                break;
-                            case PaintBundle.FONT_TYPE_MONOSPACE:
-                                if (weight == 400 && !italic) { //  for normal case
-                                    mPaint.setTypeface(Typeface.MONOSPACE);
-                                } else {
-                                    mPaint.setTypeface(
-                                            Typeface.create(Typeface.MONOSPACE, weight, italic));
-                                }
-
-                                break;
-                        }
-                    }
-
-                    @Override
-                    public void setStrokeWidth(float width) {
-                        mPaint.setStrokeWidth(width);
-                    }
-
-                    @Override
-                    public void setColor(int color) {
-                        mPaint.setColor(color);
-                    }
-
-                    @Override
-                    public void setStrokeCap(int cap) {
-                        mPaint.setStrokeCap(Paint.Cap.values()[cap]);
-                    }
-
-                    @Override
-                    public void setStyle(int style) {
-                        mPaint.setStyle(Paint.Style.values()[style]);
-                    }
-
-                    @Override
-                    public void setShader(int shaderId) {
-                        // TODO this stuff should check the shader creation
-                        if (shaderId == 0) {
-                            mPaint.setShader(null);
-                            return;
-                        }
-                        ShaderData data = getShaderData(shaderId);
-                        if (data == null) {
-                            return;
-                        }
-                        RuntimeShader shader = new RuntimeShader(getText(data.getShaderTextId()));
-                        String[] names = data.getUniformFloatNames();
-                        for (int i = 0; i < names.length; i++) {
-                            String name = names[i];
-                            float[] val = data.getUniformFloats(name);
-                            shader.setFloatUniform(name, val);
-                        }
-                        names = data.getUniformIntegerNames();
-                        for (int i = 0; i < names.length; i++) {
-                            String name = names[i];
-                            int[] val = data.getUniformInts(name);
-                            shader.setIntUniform(name, val);
-                        }
-                        names = data.getUniformBitmapNames();
-                        for (int i = 0; i < names.length; i++) {
-                            String name = names[i];
-                            int val = data.getUniformBitmapId(name);
-                        }
-                        mPaint.setShader(shader);
-                    }
-
-                    @Override
-                    public void setImageFilterQuality(int quality) {
-                        Utils.log(" quality =" + quality);
-                        mPaint.setFilterBitmap(quality == 1);
-                    }
-
-                    @Override
-                    public void setBlendMode(int mode) {
-                        mPaint.setBlendMode(origamiToBlendMode(mode));
-                    }
-
-                    @Override
-                    public void setAlpha(float a) {
-                        mPaint.setAlpha((int) (255 * a));
-                    }
-
-                    @Override
-                    public void setStrokeMiter(float miter) {
-                        mPaint.setStrokeMiter(miter);
-                    }
-
-                    @Override
-                    public void setStrokeJoin(int join) {
-                        mPaint.setStrokeJoin(Paint.Join.values()[join]);
-                    }
-
-                    @Override
-                    public void setFilterBitmap(boolean filter) {
-                        mPaint.setFilterBitmap(filter);
-                    }
-
-                    @Override
-                    public void setAntiAlias(boolean aa) {
-                        mPaint.setAntiAlias(aa);
-                    }
-
-                    @Override
-                    public void clear(long mask) {
-                        if ((mask & (1L << PaintBundle.COLOR_FILTER)) != 0) {
-                            mPaint.setColorFilter(null);
-                        }
-                    }
-
-                    Shader.TileMode[] mTileModes =
-                            new Shader.TileMode[] {
-                                Shader.TileMode.CLAMP,
-                                Shader.TileMode.REPEAT,
-                                Shader.TileMode.MIRROR
-                            };
-
-                    @Override
-                    public void setLinearGradient(
-                            @NonNull int[] colors,
-                            @NonNull float[] stops,
-                            float startX,
-                            float startY,
-                            float endX,
-                            float endY,
-                            int tileMode) {
-                        mPaint.setShader(
-                                new LinearGradient(
-                                        startX,
-                                        startY,
-                                        endX,
-                                        endY,
-                                        colors,
-                                        stops,
-                                        mTileModes[tileMode]));
-                    }
-
-                    @Override
-                    public void setRadialGradient(
-                            @NonNull int[] colors,
-                            @NonNull float[] stops,
-                            float centerX,
-                            float centerY,
-                            float radius,
-                            int tileMode) {
-                        mPaint.setShader(
-                                new RadialGradient(
-                                        centerX,
-                                        centerY,
-                                        radius,
-                                        colors,
-                                        stops,
-                                        mTileModes[tileMode]));
-                    }
-
-                    @Override
-                    public void setSweepGradient(
-                            @NonNull int[] colors,
-                            @NonNull float[] stops,
-                            float centerX,
-                            float centerY) {
-                        mPaint.setShader(new SweepGradient(centerX, centerY, colors, stops));
-                    }
-
-                    @Override
-                    public void setColorFilter(int color, int mode) {
-                        PorterDuff.Mode pmode = origamiToPorterDuffMode(mode);
-                        if (pmode != null) {
-                            mPaint.setColorFilter(new PorterDuffColorFilter(color, pmode));
-                        }
-                    }
-                });
+        paintData.applyPaintChange(this, mCachedPaintChanges);
     }
 
     @Override
@@ -774,7 +773,8 @@
         return path;
     }
 
-    private String getText(int id) {
+    @Override
+    public @Nullable String getText(int id) {
         return (String) mContext.mRemoteComposeState.getFromId(id);
     }
 
diff --git a/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidRemoteContext.java b/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidRemoteContext.java
index 0fb0a28..9d385dd 100644
--- a/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidRemoteContext.java
+++ b/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidRemoteContext.java
@@ -21,6 +21,7 @@
 import android.graphics.BitmapFactory;
 import android.graphics.Canvas;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.widget.remotecompose.core.RemoteContext;
 import com.android.internal.widget.remotecompose.core.TouchListener;
 import com.android.internal.widget.remotecompose.core.VariableSupport;
@@ -40,7 +41,8 @@
  *
  * <p>This is used to play the RemoteCompose operations on Android.
  */
-class AndroidRemoteContext extends RemoteContext {
+@VisibleForTesting
+public class AndroidRemoteContext extends RemoteContext {
 
     public void useCanvas(Canvas canvas) {
         if (mPaintContext == null) {
@@ -121,6 +123,40 @@
         mVarNameHashMap.put(integerName, null);
     }
 
+    @Override
+    public void setNamedFloatOverride(String floatName, float value) {
+        if (mVarNameHashMap.get(floatName) != null) {
+            int id = mVarNameHashMap.get(floatName).mId;
+            overrideFloat(id, value);
+        }
+    }
+
+    @Override
+    public void clearNamedFloatOverride(String floatName) {
+        if (mVarNameHashMap.get(floatName) != null) {
+            int id = mVarNameHashMap.get(floatName).mId;
+            clearFloatOverride(id);
+        }
+        mVarNameHashMap.put(floatName, null);
+    }
+
+    @Override
+    public void setNamedDataOverride(String dataName, Object value) {
+        if (mVarNameHashMap.get(dataName) != null) {
+            int id = mVarNameHashMap.get(dataName).mId;
+            overrideData(id, value);
+        }
+    }
+
+    @Override
+    public void clearNamedDataOverride(String dataName) {
+        if (mVarNameHashMap.get(dataName) != null) {
+            int id = mVarNameHashMap.get(dataName).mId;
+            clearDataOverride(id);
+        }
+        mVarNameHashMap.put(dataName, null);
+    }
+
     /**
      * Override a color to force it to be the color provided
      *
@@ -236,6 +272,10 @@
         mRemoteComposeState.overrideInteger(id, value);
     }
 
+    public void overrideData(int id, Object value) {
+        mRemoteComposeState.overrideData(id, value);
+    }
+
     public void clearDataOverride(int id) {
         mRemoteComposeState.clearDataOverride(id);
     }
@@ -244,6 +284,10 @@
         mRemoteComposeState.clearIntegerOverride(id);
     }
 
+    public void clearFloatOverride(int id) {
+        mRemoteComposeState.clearFloatOverride(id);
+    }
+
     @Override
     public String getText(int id) {
         return (String) mRemoteComposeState.getFromId(id);
diff --git a/core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java b/core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java
index c7b1166..da65a9c 100644
--- a/core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java
+++ b/core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java
@@ -16,10 +16,12 @@
 package com.android.internal.widget.remotecompose.player.platform;
 
 import android.content.Context;
+import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.Point;
 import android.util.AttributeSet;
+import android.view.Choreographer;
 import android.view.MotionEvent;
 import android.view.VelocityTracker;
 import android.view.View;
@@ -44,6 +46,22 @@
     boolean mHasClickAreas = false;
     Point mActionDownPoint = new Point(0, 0);
     AndroidRemoteContext mARContext = new AndroidRemoteContext();
+    float mDensity = 1f;
+
+    long mLastFrameDelay = 1;
+    float mMaxFrameRate = 60f; // frames per seconds
+    long mMaxFrameDelay = (long) (1000 / mMaxFrameRate);
+
+    private Choreographer mChoreographer;
+    private Choreographer.FrameCallback mFrameCallback =
+            new Choreographer.FrameCallback() {
+                @Override
+                public void doFrame(long frameTimeNanos) {
+                    mARContext.currentTime = frameTimeNanos / 1000000;
+                    mARContext.setDebug(mDebug);
+                    postInvalidateOnAnimation();
+                }
+            };
 
     public RemoteComposeCanvas(Context context) {
         super(context);
@@ -85,6 +103,9 @@
     public void setDocument(RemoteComposeDocument value) {
         mDocument = value;
         mDocument.initializeContext(mARContext);
+        mARContext.setAnimationEnabled(true);
+        mARContext.setDensity(mDensity);
+        mARContext.setUseChoreographer(true);
         setContentDescription(mDocument.getDocument().getContentDescription());
         updateClickAreas();
         requestLayout();
@@ -93,6 +114,11 @@
 
     @Override
     public void onViewAttachedToWindow(View view) {
+        if (mChoreographer == null) {
+            mChoreographer = Choreographer.getInstance();
+            mChoreographer.postFrameCallback(mFrameCallback);
+        }
+        mDensity = getContext().getResources().getDisplayMetrics().density;
         if (mDocument == null) {
             return;
         }
@@ -136,6 +162,10 @@
 
     @Override
     public void onViewDetachedFromWindow(View view) {
+        if (mChoreographer != null) {
+            mChoreographer.removeFrameCallback(mFrameCallback);
+            mChoreographer = null;
+        }
         removeAllViews();
     }
 
@@ -195,6 +225,34 @@
         }
     }
 
+    public void setLocalFloat(String name, Float content) {
+        mARContext.setNamedFloatOverride(name, content);
+        if (mDocument != null) {
+            mDocument.invalidate();
+        }
+    }
+
+    public void clearLocalFloat(String name) {
+        mARContext.clearNamedFloatOverride(name);
+        if (mDocument != null) {
+            mDocument.invalidate();
+        }
+    }
+
+    public void setLocalBitmap(String name, Bitmap content) {
+        mARContext.setNamedDataOverride(name, content);
+        if (mDocument != null) {
+            mDocument.invalidate();
+        }
+    }
+
+    public void clearLocalBitmap(String name) {
+        mARContext.clearNamedDataOverride(name);
+        if (mDocument != null) {
+            mDocument.invalidate();
+        }
+    }
+
     public int hasSensorListeners(int[] ids) {
         int count = 0;
         for (int id = RemoteContext.ID_ACCELERATION_X; id <= RemoteContext.ID_LIGHT; id++) {
@@ -236,6 +294,15 @@
         mDocument.getDocument().checkShaders(mARContext, shaderControl);
     }
 
+    /**
+     * Set to true to use the choreographer
+     *
+     * @param value
+     */
+    public void setUseChoreographer(boolean value) {
+        mARContext.setUseChoreographer(value);
+    }
+
     public interface ClickCallbacks {
         void click(int id, String metadata);
     }
@@ -414,12 +481,7 @@
             return;
         }
         long start = mEvalTime ? System.nanoTime() : 0;
-        mARContext.setAnimationEnabled(true);
-        mARContext.currentTime = System.currentTimeMillis();
-        mARContext.setDebug(mDebug);
-        float density = getContext().getResources().getDisplayMetrics().density;
         mARContext.useCanvas(canvas);
-        mARContext.setDensity(density);
         mARContext.mWidth = getWidth();
         mARContext.mHeight = getHeight();
         mDocument.paint(mARContext, mTheme);
@@ -431,8 +493,19 @@
                 mTime = System.nanoTime();
             }
         }
-        if (mDocument.needsRepaint() > 0) {
-            invalidate();
+        int nextFrame = mDocument.needsRepaint();
+        if (nextFrame > 0) {
+            mLastFrameDelay = Math.max(mMaxFrameDelay, nextFrame);
+            if (mChoreographer != null) {
+                mChoreographer.postFrameCallbackDelayed(mFrameCallback, mLastFrameDelay);
+            }
+            if (!mARContext.useChoreographer()) {
+                invalidate();
+            }
+        } else {
+            if (mChoreographer != null) {
+                mChoreographer.removeFrameCallback(mFrameCallback);
+            }
         }
         if (mEvalTime) {
             mDuration += System.nanoTime() - start;
diff --git a/core/java/com/android/server/pm/pkg/AndroidPackage.java b/core/java/com/android/server/pm/pkg/AndroidPackage.java
index 70dd10f..5fa8125 100644
--- a/core/java/com/android/server/pm/pkg/AndroidPackage.java
+++ b/core/java/com/android/server/pm/pkg/AndroidPackage.java
@@ -722,7 +722,7 @@
      * The names of packages to adopt ownership of permissions from, parsed under {@link
      * ParsingPackageUtils#TAG_ADOPT_PERMISSIONS}.
      *
-     * @see R.styleable#AndroidManifestOriginalPackage_name
+     * @see R.styleable#AndroidManifestAdoptPermissions_name
      * @hide
      */
     @NonNull
diff --git a/core/java/com/android/server/servicewatcher/ServiceWatcher.java b/core/java/com/android/server/servicewatcher/ServiceWatcher.java
index 831ff67..38872c9 100644
--- a/core/java/com/android/server/servicewatcher/ServiceWatcher.java
+++ b/core/java/com/android/server/servicewatcher/ServiceWatcher.java
@@ -214,11 +214,7 @@
 
         @Override
         public String toString() {
-            if (mComponentName == null) {
-                return "none";
-            } else {
-                return mUid + "/" + mComponentName.flattenToShortString();
-            }
+            return mUid + "/" + mComponentName.flattenToShortString();
         }
     }
 
diff --git a/core/jni/android_database_SQLiteRawStatement.cpp b/core/jni/android_database_SQLiteRawStatement.cpp
index 32c2ef7..5fa8083 100644
--- a/core/jni/android_database_SQLiteRawStatement.cpp
+++ b/core/jni/android_database_SQLiteRawStatement.cpp
@@ -81,10 +81,11 @@
     return true;
 }
 
-// This throws a SQLiteBindOrColumnIndexOutOfRangeException if the column index is out of
-// bounds.  It throws SQLiteMisuseException if the statement's column count is zero; that
-// generally occurs because the client has forgotten to call step() or the client has stepped
-// past the end of the query.  The function returns true if an exception was thrown.
+// This throws a SQLiteBindOrColumnIndexOutOfRangeException if the column index is outside the
+// bounds of the row data set.  It throws SQLiteMisuseException if the statement's data column
+// count is zero; that generally occurs because the client has forgotten to call step() or the
+// client has stepped past the end of the query.  The function returns true if an exception was
+// thrown.
 static bool throwIfInvalidColumn(JNIEnv *env, jlong stmtPtr, jint col) {
     int count = sqlite3_data_count(stmt(stmtPtr));
     if (throwIfError(env, stmtPtr)) {
@@ -106,6 +107,24 @@
     }
 }
 
+// This throws a SQLiteBindOrColumnIndexOutOfRangeException if the column index is outside the
+// bounds of the result set.  (This is not the same as the data columns in a row).  The function
+// returns true if an exception was thrown.
+static bool throwIfInvalidResultColumn(JNIEnv *env, jlong stmtPtr, jint col) {
+    int count = sqlite3_column_count(stmt(stmtPtr));
+    if (throwIfError(env, stmtPtr)) {
+        return true;
+    } else if (col < 0 || col >= count) {
+        std::string message = android::base::StringPrintf(
+            "column index %d out of bounds [0,%d]", col, count - 1);
+        char const * errmsg = sqlite3_errstr(SQLITE_RANGE);
+        throw_sqlite3_exception(env, SQLITE_RANGE, errmsg, message.c_str());
+        return true;
+    } else {
+        return false;
+    }
+}
+
 static jint bindParameterCount(JNIEnv* env, jclass, jlong stmtPtr) {
     return sqlite3_bind_parameter_count(stmt(stmtPtr));
 }
@@ -235,7 +254,7 @@
 }
 
 static jstring columnName(JNIEnv* env, jclass, jlong stmtPtr, jint col) {
-    if (throwIfInvalidColumn(env, stmtPtr, col)) {
+    if (throwIfInvalidResultColumn(env, stmtPtr, col)) {
         return nullptr;
     }
     const jchar* name = static_cast<const jchar*>(sqlite3_column_name16(stmt(stmtPtr), col));
diff --git a/core/jni/android_graphics_BLASTBufferQueue.cpp b/core/jni/android_graphics_BLASTBufferQueue.cpp
index b9c3bf7..7b61a5d 100644
--- a/core/jni/android_graphics_BLASTBufferQueue.cpp
+++ b/core/jni/android_graphics_BLASTBufferQueue.cpp
@@ -87,6 +87,38 @@
     jobject mTransactionHangObject;
 };
 
+struct {
+    jmethodID onWaitForBufferRelease;
+} gWaitForBufferReleaseCallback;
+
+class WaitForBufferReleaseCallbackWrapper
+      : public LightRefBase<WaitForBufferReleaseCallbackWrapper> {
+public:
+    explicit WaitForBufferReleaseCallbackWrapper(JNIEnv* env, jobject jobject) {
+        env->GetJavaVM(&mVm);
+        mWaitForBufferReleaseObject = env->NewGlobalRef(jobject);
+        LOG_ALWAYS_FATAL_IF(!mWaitForBufferReleaseObject, "Failed to make global ref");
+    }
+
+    ~WaitForBufferReleaseCallbackWrapper() {
+        if (mWaitForBufferReleaseObject != nullptr) {
+            getenv(mVm)->DeleteGlobalRef(mWaitForBufferReleaseObject);
+            mWaitForBufferReleaseObject = nullptr;
+        }
+    }
+
+    void onWaitForBufferRelease() {
+        JNIEnv* env = getenv(mVm);
+        getenv(mVm)->CallVoidMethod(mWaitForBufferReleaseObject,
+                                    gWaitForBufferReleaseCallback.onWaitForBufferRelease);
+        DieIfException(env, "Uncaught exception in WaitForBufferReleaseCallback.");
+    }
+
+private:
+    JavaVM* mVm;
+    jobject mWaitForBufferReleaseObject;
+};
+
 static jlong nativeCreate(JNIEnv* env, jclass clazz, jstring jName,
                           jboolean updateDestinationFrame) {
     ScopedUtfChars name(env, jName);
@@ -215,6 +247,18 @@
     return queue->setApplyToken(std::move(token));
 }
 
+static void nativeSetWaitForBufferReleaseCallback(JNIEnv* env, jclass clazz, jlong ptr,
+                                                  jobject waitForBufferReleaseCallback) {
+    sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);
+    if (waitForBufferReleaseCallback == nullptr) {
+        queue->setWaitForBufferReleaseCallback(nullptr);
+    } else {
+        sp<WaitForBufferReleaseCallbackWrapper> wrapper =
+                new WaitForBufferReleaseCallbackWrapper{env, waitForBufferReleaseCallback};
+        queue->setWaitForBufferReleaseCallback([wrapper]() { wrapper->onWaitForBufferRelease(); });
+    }
+}
+
 static const JNINativeMethod gMethods[] = {
         /* name, signature, funcPtr */
         // clang-format off
@@ -234,6 +278,9 @@
          "(JLandroid/graphics/BLASTBufferQueue$TransactionHangCallback;)V",
          (void*)nativeSetTransactionHangCallback},
         {"nativeSetApplyToken", "(JLandroid/os/IBinder;)V", (void*)nativeSetApplyToken},
+        {"nativeSetWaitForBufferReleaseCallback",
+         "(JLandroid/graphics/BLASTBufferQueue$WaitForBufferReleaseCallback;)V",
+         (void*)nativeSetWaitForBufferReleaseCallback},
         // clang-format on
 };
 
@@ -255,6 +302,10 @@
     gTransactionHangCallback.onTransactionHang =
             GetMethodIDOrDie(env, transactionHangClass, "onTransactionHang",
                              "(Ljava/lang/String;)V");
+    jclass waitForBufferReleaseClass =
+            FindClassOrDie(env, "android/graphics/BLASTBufferQueue$WaitForBufferReleaseCallback");
+    gWaitForBufferReleaseCallback.onWaitForBufferRelease =
+            GetMethodIDOrDie(env, waitForBufferReleaseClass, "onWaitForBufferRelease", "()V");
 
     return 0;
 }
diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp
index 7ef7829..dc72539 100644
--- a/core/jni/android_util_Process.cpp
+++ b/core/jni/android_util_Process.cpp
@@ -589,32 +589,6 @@
     return pri;
 }
 
-jboolean android_os_Process_setSwappiness(JNIEnv *env, jobject clazz,
-                                          jint pid, jboolean is_increased)
-{
-    char text[64];
-
-    if (is_increased) {
-        strcpy(text, "/sys/fs/cgroup/memory/sw/tasks");
-    } else {
-        strcpy(text, "/sys/fs/cgroup/memory/tasks");
-    }
-
-    struct stat st;
-    if (stat(text, &st) || !S_ISREG(st.st_mode)) {
-        return false;
-    }
-
-    int fd = open(text, O_WRONLY | O_CLOEXEC);
-    if (fd >= 0) {
-        sprintf(text, "%" PRId32, pid);
-        write(fd, text, strlen(text));
-        close(fd);
-    }
-
-    return true;
-}
-
 void android_os_Process_setArgV0(JNIEnv* env, jobject clazz, jstring name)
 {
     if (name == NULL) {
@@ -1396,7 +1370,6 @@
         {"getProcessGroup", "(I)I", (void*)android_os_Process_getProcessGroup},
         {"createProcessGroup", "(II)I", (void*)android_os_Process_createProcessGroup},
         {"getExclusiveCores", "()[I", (void*)android_os_Process_getExclusiveCores},
-        {"setSwappiness", "(IZ)Z", (void*)android_os_Process_setSwappiness},
         {"setArgV0Native", "(Ljava/lang/String;)V", (void*)android_os_Process_setArgV0},
         {"setUid", "(I)I", (void*)android_os_Process_setUid},
         {"setGid", "(I)I", (void*)android_os_Process_setGid},
diff --git a/core/jni/android_view_SurfaceControlActivePictureListener.cpp b/core/jni/android_view_SurfaceControlActivePictureListener.cpp
index 91849c1..15132db 100644
--- a/core/jni/android_view_SurfaceControlActivePictureListener.cpp
+++ b/core/jni/android_view_SurfaceControlActivePictureListener.cpp
@@ -106,12 +106,11 @@
     }
 
     status_t startListening() {
-        // TODO(b/337330263): Make SF multiple-listener capable
-        return SurfaceComposerClient::setActivePictureListener(this);
+        return SurfaceComposerClient::addActivePictureListener(this);
     }
 
     status_t stopListening() {
-        return SurfaceComposerClient::setActivePictureListener(nullptr);
+        return SurfaceComposerClient::removeActivePictureListener(this);
     }
 
 protected:
diff --git a/core/jni/android_window_ScreenCapture.cpp b/core/jni/android_window_ScreenCapture.cpp
index 1a52fb7..5657fa1 100644
--- a/core/jni/android_window_ScreenCapture.cpp
+++ b/core/jni/android_window_ScreenCapture.cpp
@@ -110,13 +110,19 @@
         captureResults.fenceResult.value()->waitForever(LOG_TAG);
         jobject jhardwareBuffer = android_hardware_HardwareBuffer_createFromAHardwareBuffer(
                 env, captureResults.buffer->toAHardwareBuffer());
+        jobject jGainmap = nullptr;
+        if (captureResults.optionalGainMap) {
+            jGainmap = android_hardware_HardwareBuffer_createFromAHardwareBuffer(
+                    env, captureResults.optionalGainMap->toAHardwareBuffer());
+        }
         jobject screenshotHardwareBuffer =
                 env->CallStaticObjectMethod(gScreenshotHardwareBufferClassInfo.clazz,
                                             gScreenshotHardwareBufferClassInfo.builder,
                                             jhardwareBuffer,
                                             static_cast<jint>(captureResults.capturedDataspace),
                                             captureResults.capturedSecureLayers,
-                                            captureResults.capturedHdrLayers);
+                                            captureResults.capturedHdrLayers, jGainmap,
+                                            captureResults.hdrSdrRatio);
         checkAndClearException(env, "builder");
         env->CallVoidMethod(consumer.get(), gConsumerClassInfo.accept, screenshotHardwareBuffer,
                             fenceStatus(captureResults.fenceResult));
@@ -340,7 +346,8 @@
             MakeGlobalRefOrDie(env, screenshotGraphicsBufferClazz);
     gScreenshotHardwareBufferClassInfo.builder =
             GetStaticMethodIDOrDie(env, screenshotGraphicsBufferClazz, "createFromNative",
-                                   "(Landroid/hardware/HardwareBuffer;IZZ)Landroid/window/"
+                                   "(Landroid/hardware/HardwareBuffer;IZZLandroid/hardware/"
+                                   "HardwareBuffer;F)Landroid/window/"
                                    "ScreenCapture$ScreenshotHardwareBuffer;");
 
     return err;
diff --git a/core/jni/com_android_internal_content_FileSystemUtils.cpp b/core/jni/com_android_internal_content_FileSystemUtils.cpp
index 76ead2a..48c92c8 100644
--- a/core/jni/com_android_internal_content_FileSystemUtils.cpp
+++ b/core/jni/com_android_internal_content_FileSystemUtils.cpp
@@ -21,8 +21,6 @@
 #include <android-base/file.h>
 #include <android-base/hex.h>
 #include <android-base/unique_fd.h>
-#include <bionic/macros.h>
-#include <errno.h>
 #include <fcntl.h>
 #include <inttypes.h>
 #include <linux/fs.h>
@@ -48,8 +46,8 @@
         return false;
     }
 
-    start = align_up(start, blockSize);
-    end = align_down(end, blockSize);
+    start = __builtin_align_up(start, blockSize);
+    end = __builtin_align_down(end, blockSize);
 
     uint64_t alignedLength;
     if (__builtin_sub_overflow(end, start, &alignedLength)) {
@@ -67,7 +65,7 @@
     int result =
             fallocate(fd.get(), FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, start, alignedLength);
     if (result < 0) {
-        ALOGE("fallocate failed to punch hole, error:%d", errno);
+        ALOGE("fallocate failed to punch hole: %m");
         return false;
     }
 
@@ -78,7 +76,7 @@
                 const std::vector<Elf64_Phdr> &programHeaders) {
     struct stat64 beforePunch;
     if (int result = lstat64(filePath, &beforePunch); result != 0) {
-        ALOGE("lstat64 failed for filePath %s, error:%d", filePath, errno);
+        ALOGE("lstat64 failed for filePath %s: %m", filePath);
         return false;
     }
 
@@ -190,7 +188,7 @@
     IF_ALOGD() {
         struct stat64 afterPunch;
         if (int result = lstat64(filePath, &afterPunch); result != 0) {
-            ALOGD("lstat64 failed for filePath %s, error:%d", filePath, errno);
+            ALOGD("lstat64 failed for filePath %s: %m", filePath);
             return false;
         }
         ALOGD("Size after punching holes st_blocks: %" PRIu64 ", st_blksize: %" PRIu64
@@ -269,7 +267,7 @@
 
     struct stat64 beforePunch;
     if (int result = lstat64(filePath, &beforePunch); result != 0) {
-        ALOGE("lstat64 failed for filePath %s, error:%d", filePath, errno);
+        ALOGE("lstat64 failed for filePath %s: %m", filePath);
         return false;
     }
 
@@ -348,7 +346,7 @@
     IF_ALOGD() {
         struct stat64 afterPunch;
         if (int result = lstat64(filePath, &afterPunch); result != 0) {
-            ALOGD("lstat64 failed for filePath %s, error:%d", filePath, errno);
+            ALOGD("lstat64 failed for filePath %s: %m", filePath);
             return false;
         }
         ALOGD("punchHolesInApk:: Size after punching holes st_blocks: %" PRIu64
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index aeaeeca..8c7b335 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -88,8 +88,8 @@
 #include "nativebridge/native_bridge.h"
 
 #if defined(__BIONIC__)
+#include <android/dlext_private.h>
 extern "C" void android_reset_stack_guards();
-extern "C" void android_set_16kb_appcompat_mode(bool enable_app_compat);
 #endif
 
 namespace {
diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto
index 2e0fe9e..7e9d623 100644
--- a/core/proto/android/providers/settings/secure.proto
+++ b/core/proto/android/providers/settings/secure.proto
@@ -105,6 +105,7 @@
         optional SettingProto accessibility_gesture_targets = 57 [ (android.privacy).dest = DEST_AUTOMATIC ];
         optional SettingProto display_daltonizer_saturation_level = 58 [ (android.privacy).dest = DEST_AUTOMATIC ];
         optional SettingProto accessibility_key_gesture_targets = 59 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto hct_rect_prompt_status = 60 [ (android.privacy).dest = DEST_AUTOMATIC ];
 
     }
     optional Accessibility accessibility = 2;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 6b8056c..e133ca4 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -4535,11 +4535,11 @@
     <permission android:name="android.permission.REQUEST_COMPANION_PROFILE_GLASSES"
         android:protectionLevel="normal" />
 
-    <!-- Allows application to request to be associated with a virtual display capable of streaming
+    <!-- Allows application to request to be associated with a virtual device capable of streaming
          Android applications
          ({@link android.companion.AssociationRequest#DEVICE_PROFILE_APP_STREAMING})
          by {@link android.companion.CompanionDeviceManager}.
-        <p>Not for use by third-party applications.
+         <p>Not for use by third-party applications.
     -->
     <permission android:name="android.permission.REQUEST_COMPANION_PROFILE_APP_STREAMING"
                 android:protectionLevel="signature|privileged" />
@@ -4547,16 +4547,25 @@
     <!-- Allows application to request to stream content from an Android host to a nearby device
          ({@link android.companion.AssociationRequest#DEVICE_PROFILE_NEARBY_DEVICE_STREAMING})
          by {@link android.companion.CompanionDeviceManager}.
-        <p>Not for use by third-party applications.
+         <p>Not for use by third-party applications.
     -->
     <permission android:name="android.permission.REQUEST_COMPANION_PROFILE_NEARBY_DEVICE_STREAMING"
         android:protectionLevel="signature|privileged" />
 
+    <!-- Allows application to request to stream content from an Android host to a nearby device
+         ({@link android.companion.AssociationRequest#DEVICE_PROFILE_SENSOR_DEVICE_STREAMING})
+         by {@link android.companion.CompanionDeviceManager}.
+         <p>Not for use by third-party applications.
+         @FlaggedApi(android.companion.virtualdevice.flags.Flags.FLAG_ENABLE_LIMITED_VDM_ROLE)
+    -->
+    <permission android:name="android.permission.REQUEST_COMPANION_PROFILE_SENSOR_DEVICE_STREAMING"
+        android:protectionLevel="signature|privileged" />
+
     <!-- Allows application to request to be associated with a vehicle head unit capable of
          automotive projection
          ({@link android.companion.AssociationRequest#DEVICE_PROFILE_AUTOMOTIVE_PROJECTION})
          by {@link android.companion.CompanionDeviceManager}.
-        <p>Not for use by third-party applications.
+         <p>Not for use by third-party applications.
     -->
     <permission android:name="android.permission.REQUEST_COMPANION_PROFILE_AUTOMOTIVE_PROJECTION"
                 android:protectionLevel="internal|role" />
@@ -4565,7 +4574,7 @@
          and/or data with other devices, such as notifications, photos and media
          ({@link android.companion.AssociationRequest#DEVICE_PROFILE_COMPUTER})
          by {@link android.companion.CompanionDeviceManager}.
-        <p>Not for use by third-party applications.
+         <p>Not for use by third-party applications.
     -->
     <permission android:name="android.permission.REQUEST_COMPANION_PROFILE_COMPUTER"
                 android:protectionLevel="signature|privileged" />
@@ -5639,11 +5648,21 @@
 
     <!-- Allows an application to subscribe to device locked and keyguard locked (i.e., showing)
         state.
-         <p>Protection level: signature|role
-         <p>Intended for use by ROLE_ASSISTANT and signature apps only.
+         <p>Protection level: signature|module|role
+         <p>Intended for use by ROLE_ASSISTANT, VDM, and signature apps only.
     -->
     <permission android:name="android.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE"
-                android:protectionLevel="signature|module|role"/>
+                android:protectionLevel="signature|module|role"
+                android:featureFlag="!android.security.subscribe_to_keyguard_locked_state_perm_priv_flag"/>
+
+    <!-- Allows an application to subscribe to device locked and keyguard locked (i.e., showing)
+        state.
+         <p>Protection level: signature|privileged|module|role
+         <p>Intended for use by ROLE_ASSISTANT, VDM, and signature / privileged apps only.
+    -->
+    <permission android:name="android.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE"
+                android:protectionLevel="signature|privileged|module|role"
+                android:featureFlag="android.security.subscribe_to_keyguard_locked_state_perm_priv_flag"/>
 
     <!-- Must be required by a {@link android.service.autofill.AutofillService},
          to ensure that only the system can bind to it.
@@ -8288,20 +8307,21 @@
         android:featureFlag="android.app.appfunctions.flags.enable_app_function_manager"
         android:protectionLevel="signature" />
 
-    <!-- @SystemApi Allows a trusted application to perform actions on behalf of users inside of
+    <!-- Allows a trusted application to perform actions on behalf of users inside of
          applications with privacy guarantees from the system.
          <p>This permission is currently only granted to system packages in the
          {@link android.app.role.SYSTEM_UI_INTELLIGENCE} role which complies with privacy
          requirements outlined in the Android CDD section "9.8.6 Content Capture".
          <p>Apps are not able to opt-out from caller having this permission.
          <p>Protection level: internal|role
+         @SystemApi
          @hide
-         @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager")  -->
+         @FlaggedApi(android.app.appfunctions.flags.Flags.FLAG_ENABLE_APP_FUNCTION_MANAGER)  -->
     <permission android:name="android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED"
         android:featureFlag="android.app.appfunctions.flags.enable_app_function_manager"
         android:protectionLevel="internal|role" />
 
-    <!-- @SystemApi Allows an application to perform actions on behalf of users inside of
+    <!-- Allows an application to perform actions on behalf of users inside of
          applications.
          <p>This permission is currently only granted to preinstalled / system apps having the
          {@link android.app.role.ASSISTANT} role.
@@ -8309,8 +8329,7 @@
          limiting to only callers with {@link android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED}
          instead.
          <p>Protection level: internal|role
-         @hide
-         @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager")  -->
+         @FlaggedApi(android.app.appfunctions.flags.Flags.FLAG_ENABLE_APP_FUNCTION_MANAGER)  -->
     <permission android:name="android.permission.EXECUTE_APP_FUNCTIONS"
         android:featureFlag="android.app.appfunctions.flags.enable_app_function_manager"
         android:protectionLevel="internal|role" />
@@ -9319,7 +9338,11 @@
                  android:permission="android.permission.BIND_JOB_SERVICE" >
         </service>
 
-        <service android:name="com.android.server.profcollect.ProfcollectForwardingService$ProfcollectBGJobService"
+        <service android:name="com.android.server.profcollect.ProfcollectForwardingService$PeriodicTraceJobService"
+                 android:permission="android.permission.BIND_JOB_SERVICE" >
+        </service>
+
+        <service android:name="com.android.server.profcollect.ProfcollectForwardingService$ReportProcessJobService"
                  android:permission="android.permission.BIND_JOB_SERVICE" >
         </service>
 
diff --git a/core/res/res/color-watch-v36/btn_material_filled_background_color.xml b/core/res/res/color/btn_material_filled_background_color_watch.xml
similarity index 92%
rename from core/res/res/color-watch-v36/btn_material_filled_background_color.xml
rename to core/res/res/color/btn_material_filled_background_color_watch.xml
index 39b5b10..78547bc 100644
--- a/core/res/res/color-watch-v36/btn_material_filled_background_color.xml
+++ b/core/res/res/color/btn_material_filled_background_color_watch.xml
@@ -16,7 +16,7 @@
 
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
     <item android:state_enabled="false"
-          android:alpha="?attr/disabledAlpha"
+          android:alpha="@dimen/disabled_alpha_wear_material3"
           android:color="@color/materialColorOnSurface" />
     <item android:color="@color/materialColorPrimary" />
 </selector>
\ No newline at end of file
diff --git a/core/res/res/color-watch-v36/btn_material_filled_content_color.xml b/core/res/res/color/btn_material_filled_content_color_watch.xml
similarity index 91%
rename from core/res/res/color-watch-v36/btn_material_filled_content_color.xml
rename to core/res/res/color/btn_material_filled_content_color_watch.xml
index a70586b..3a4ec3c 100644
--- a/core/res/res/color-watch-v36/btn_material_filled_content_color.xml
+++ b/core/res/res/color/btn_material_filled_content_color_watch.xml
@@ -16,7 +16,7 @@
 
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
     <item android:state_enabled="false"
-          android:alpha="?attr/primaryContentAlpha"
+          android:alpha="@dimen/primary_content_alpha_wear_material3"
           android:color="@color/materialColorOnSurface" />
     <item android:color="@color/materialColorOnPrimary" />
 </selector>
\ No newline at end of file
diff --git a/core/res/res/color-watch-v36/btn_material_filled_tonal_background_color.xml b/core/res/res/color/btn_material_filled_tonal_background_color_watch.xml
similarity index 92%
rename from core/res/res/color-watch-v36/btn_material_filled_tonal_background_color.xml
rename to core/res/res/color/btn_material_filled_tonal_background_color_watch.xml
index e2e274e..e9b7e4c 100644
--- a/core/res/res/color-watch-v36/btn_material_filled_tonal_background_color.xml
+++ b/core/res/res/color/btn_material_filled_tonal_background_color_watch.xml
@@ -16,7 +16,7 @@
 
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
     <item android:state_enabled="false"
-          android:alpha="?attr/disabledAlpha"
+          android:alpha="@dimen/disabled_alpha_wear_material3"
           android:color="@color/materialColorOnSurface" />
     <item android:color="@color/materialColorSurfaceContainer" />
 </selector>
\ No newline at end of file
diff --git a/core/res/res/color-watch-v36/btn_material_filled_tonal_content_color.xml b/core/res/res/color/btn_material_filled_tonal_content_color_watch.xml
similarity index 91%
rename from core/res/res/color-watch-v36/btn_material_filled_tonal_content_color.xml
rename to core/res/res/color/btn_material_filled_tonal_content_color_watch.xml
index 32de688..8b5deda 100644
--- a/core/res/res/color-watch-v36/btn_material_filled_tonal_content_color.xml
+++ b/core/res/res/color/btn_material_filled_tonal_content_color_watch.xml
@@ -16,7 +16,7 @@
 
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
     <item android:state_enabled="false"
-          android:alpha="?attr/primaryContentAlpha"
+          android:alpha="@dimen/primary_content_alpha_wear_material3"
           android:color="@color/materialColorOnSurface" />
     <item android:color="@color/materialColorOnSurface" />
 </selector>
\ No newline at end of file
diff --git a/core/res/res/color-watch-v36/btn_material_outlined_background_color.xml b/core/res/res/color/btn_material_outlined_background_color_watch.xml
similarity index 92%
rename from core/res/res/color-watch-v36/btn_material_outlined_background_color.xml
rename to core/res/res/color/btn_material_outlined_background_color_watch.xml
index 3f43ca7..f9c9a3f 100644
--- a/core/res/res/color-watch-v36/btn_material_outlined_background_color.xml
+++ b/core/res/res/color/btn_material_outlined_background_color_watch.xml
@@ -16,7 +16,7 @@
 
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
     <item android:state_enabled="false"
-          android:alpha="?attr/disabledAlpha"
+          android:alpha="@dimen/disabled_alpha_wear_material3"
           android:color="@color/materialColorOnSurface" />
     <item android:color="@color/materialColorOutline" />
 </selector>
diff --git a/core/res/res/drawable-watch-v36/dialog_alert_button_negative.xml b/core/res/res/drawable-watch-v36/dialog_alert_button_negative.xml
deleted file mode 100644
index c155ba1..0000000
--- a/core/res/res/drawable-watch-v36/dialog_alert_button_negative.xml
+++ /dev/null
@@ -1,22 +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.
-  -->
-
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:drawable="@drawable/dialog_alert_button_background_negative"/>
-    <item
-        android:drawable="@drawable/ic_close"
-        android:gravity="center" />
-</layer-list>
\ No newline at end of file
diff --git a/core/res/res/drawable-watch-v36/btn_background_material_filled_tonal.xml b/core/res/res/drawable/btn_background_material_filled_tonal_watch.xml
similarity index 96%
rename from core/res/res/drawable-watch-v36/btn_background_material_filled_tonal.xml
rename to core/res/res/drawable/btn_background_material_filled_tonal_watch.xml
index fbd6973..69c467b 100644
--- a/core/res/res/drawable-watch-v36/btn_background_material_filled_tonal.xml
+++ b/core/res/res/drawable/btn_background_material_filled_tonal_watch.xml
@@ -18,7 +18,7 @@
         android:color="?attr/colorControlHighlight">
     <item>
         <shape android:shape="rectangle">
-            <solid android:color="@color/btn_material_filled_tonal_background_color"/>
+            <solid android:color="@color/btn_material_filled_tonal_background_color_watch"/>
             <corners android:radius="@dimen/config_wearMaterial3_buttonCornerRadius"/>
             <size
                 android:width="@dimen/btn_material_width"
diff --git a/core/res/res/drawable-watch-v36/btn_background_material_filled.xml b/core/res/res/drawable/btn_background_material_filled_watch.xml
similarity index 97%
rename from core/res/res/drawable-watch-v36/btn_background_material_filled.xml
rename to core/res/res/drawable/btn_background_material_filled_watch.xml
index 6e74f64..76ba847 100644
--- a/core/res/res/drawable-watch-v36/btn_background_material_filled.xml
+++ b/core/res/res/drawable/btn_background_material_filled_watch.xml
@@ -18,7 +18,7 @@
         android:color="?attr/colorControlHighlight">
     <item>
         <shape android:shape="rectangle">
-            <solid android:color="@color/btn_material_filled_background_color"/>
+            <solid android:color="@color/btn_material_filled_background_color_watch"/>
             <corners android:radius="@dimen/config_wearMaterial3_buttonCornerRadius"/>
             <size
                 android:width="@dimen/btn_material_width"
diff --git a/core/res/res/drawable-watch-v36/btn_background_material_outlined.xml b/core/res/res/drawable/btn_background_material_outlined_watch.xml
similarity index 97%
rename from core/res/res/drawable-watch-v36/btn_background_material_outlined.xml
rename to core/res/res/drawable/btn_background_material_outlined_watch.xml
index 7bc4060..16190aa 100644
--- a/core/res/res/drawable-watch-v36/btn_background_material_outlined.xml
+++ b/core/res/res/drawable/btn_background_material_outlined_watch.xml
@@ -30,7 +30,7 @@
             <corners android:radius="@dimen/config_wearMaterial3_buttonCornerRadius"/>
             <stroke
                 android:width="1dp"
-                android:color="@color/btn_material_outlined_background_color" />
+                android:color="@color/btn_material_outlined_background_color_watch" />
             <size
                 android:width="@dimen/btn_material_width"
                 android:height="@dimen/btn_material_height" />
diff --git a/core/res/res/drawable-watch-v36/btn_background_material_text.xml b/core/res/res/drawable/btn_background_material_text_watch.xml
similarity index 100%
rename from core/res/res/drawable-watch-v36/btn_background_material_text.xml
rename to core/res/res/drawable/btn_background_material_text_watch.xml
diff --git a/core/res/res/drawable-watch-v36/dialog_alert_button_background_negative.xml b/core/res/res/drawable/dialog_alert_button_background_negative_watch.xml
similarity index 97%
rename from core/res/res/drawable-watch-v36/dialog_alert_button_background_negative.xml
rename to core/res/res/drawable/dialog_alert_button_background_negative_watch.xml
index 0314bbe..495fa4a 100644
--- a/core/res/res/drawable-watch-v36/dialog_alert_button_background_negative.xml
+++ b/core/res/res/drawable/dialog_alert_button_background_negative_watch.xml
@@ -17,7 +17,7 @@
 
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
        android:shape="rectangle">
-    <solid android:color="@color/btn_material_filled_tonal_background_color"/>
+    <solid android:color="@color/btn_material_filled_tonal_background_color_watch"/>
     <corners android:radius="@dimen/config_wearMaterial3_bottomDialogCornerRadius" />
     <size
         android:width="@dimen/dialog_btn_negative_width"
diff --git a/core/res/res/drawable-watch-v36/dialog_alert_button_background_positive.xml b/core/res/res/drawable/dialog_alert_button_background_positive_watch.xml
similarity index 97%
rename from core/res/res/drawable-watch-v36/dialog_alert_button_background_positive.xml
rename to core/res/res/drawable/dialog_alert_button_background_positive_watch.xml
index 92262fb..20a4c0b 100644
--- a/core/res/res/drawable-watch-v36/dialog_alert_button_background_positive.xml
+++ b/core/res/res/drawable/dialog_alert_button_background_positive_watch.xml
@@ -21,7 +21,7 @@
         android:pivotX="50%"
         android:pivotY="50%">
     <shape android:shape="rectangle">
-        <solid android:color="@color/btn_material_filled_background_color"/>
+        <solid android:color="@color/btn_material_filled_background_color_watch"/>
         <corners android:radius="200dp" />
         <size
             android:width="63dp"
diff --git a/core/res/res/drawable-watch-v36/dialog_alert_button_positive.xml b/core/res/res/drawable/dialog_alert_button_negative_watch.xml
similarity index 91%
rename from core/res/res/drawable-watch-v36/dialog_alert_button_positive.xml
rename to core/res/res/drawable/dialog_alert_button_negative_watch.xml
index 01ab073..1776962 100644
--- a/core/res/res/drawable-watch-v36/dialog_alert_button_positive.xml
+++ b/core/res/res/drawable/dialog_alert_button_negative_watch.xml
@@ -15,8 +15,8 @@
   -->
 
 <layer-list xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:drawable="@drawable/dialog_alert_button_background_positive"/>
+    <item android:drawable="@drawable/dialog_alert_button_background_negative_watch"/>
     <item
-        android:drawable="@drawable/ic_check"
+        android:drawable="@drawable/ic_close_watch"
         android:gravity="center" />
 </layer-list>
\ No newline at end of file
diff --git a/core/res/res/drawable-watch-v36/dialog_alert_button_positive.xml b/core/res/res/drawable/dialog_alert_button_positive_watch.xml
similarity index 91%
copy from core/res/res/drawable-watch-v36/dialog_alert_button_positive.xml
copy to core/res/res/drawable/dialog_alert_button_positive_watch.xml
index 01ab073..ab08e2a 100644
--- a/core/res/res/drawable-watch-v36/dialog_alert_button_positive.xml
+++ b/core/res/res/drawable/dialog_alert_button_positive_watch.xml
@@ -15,8 +15,8 @@
   -->
 
 <layer-list xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:drawable="@drawable/dialog_alert_button_background_positive"/>
+    <item android:drawable="@drawable/dialog_alert_button_background_positive_watch"/>
     <item
-        android:drawable="@drawable/ic_check"
+        android:drawable="@drawable/ic_check_watch"
         android:gravity="center" />
 </layer-list>
\ No newline at end of file
diff --git a/core/res/res/drawable-watch-v36/ic_check.xml b/core/res/res/drawable/ic_check_watch.xml
similarity index 93%
rename from core/res/res/drawable-watch-v36/ic_check.xml
rename to core/res/res/drawable/ic_check_watch.xml
index 7b01e64..2fc161f 100644
--- a/core/res/res/drawable-watch-v36/ic_check.xml
+++ b/core/res/res/drawable/ic_check_watch.xml
@@ -19,7 +19,7 @@
         android:height="28dp"
         android:viewportWidth="960"
         android:viewportHeight="960"
-        android:tint="@color/btn_material_filled_content_color">
-    <path android:fillColor="@color/btn_material_filled_content_color"
+        android:tint="@color/btn_material_filled_content_color_watch">
+    <path android:fillColor="@color/btn_material_filled_content_color_watch"
           android:pathData="M382,597.87L716.7,263.17Q730.37,249.5 748.76,249.5Q767.15,249.5 780.83,263.17Q794.5,276.85 794.5,295.62Q794.5,314.39 780.83,328.07L414.07,695.59Q400.39,709.26 382,709.26Q363.61,709.26 349.93,695.59L178.41,524.07Q164.74,510.39 165.12,491.62Q165.5,472.85 179.17,459.17Q192.85,445.5 211.62,445.5Q230.39,445.5 244.07,459.17L382,597.87Z"/>
 </vector>
diff --git a/core/res/res/drawable-watch-v36/ic_close.xml b/core/res/res/drawable/ic_close_watch.xml
similarity index 97%
rename from core/res/res/drawable-watch-v36/ic_close.xml
rename to core/res/res/drawable/ic_close_watch.xml
index 1f3da36..55e7213 100644
--- a/core/res/res/drawable-watch-v36/ic_close.xml
+++ b/core/res/res/drawable/ic_close_watch.xml
@@ -19,7 +19,7 @@
         android:height="28dp"
         android:viewportWidth="960"
         android:viewportHeight="960"
-        android:tint="@color/btn_material_filled_tonal_content_color">
-    <path android:fillColor="@color/btn_material_filled_tonal_content_color"
+        android:tint="@color/btn_material_filled_tonal_content_color_watch">
+    <path android:fillColor="@color/btn_material_filled_tonal_content_color_watch"
           android:pathData="M480,543.65L287.83,735.83Q275.15,748.5 256,748.5Q236.85,748.5 224.17,735.83Q211.5,723.15 211.5,704Q211.5,684.85 224.17,672.17L416.35,480L224.17,287.83Q211.5,275.15 211.5,256Q211.5,236.85 224.17,224.17Q236.85,211.5 256,211.5Q275.15,211.5 287.83,224.17L480,416.35L672.17,224.17Q684.85,211.5 704,211.5Q723.15,211.5 735.83,224.17Q748.5,236.85 748.5,256Q748.5,275.15 735.83,287.83L543.65,480L735.83,672.17Q748.5,684.85 748.5,704Q748.5,723.15 735.83,735.83Q723.15,748.5 704,748.5Q684.85,748.5 672.17,735.83L480,543.65Z"/>
 </vector>
diff --git a/core/res/res/drawable-watch-v36/progress_ring_wear_material3.xml b/core/res/res/drawable/progress_ring_watch.xml
similarity index 100%
rename from core/res/res/drawable-watch-v36/progress_ring_wear_material3.xml
rename to core/res/res/drawable/progress_ring_watch.xml
diff --git a/core/res/res/layout-watch-v36/alert_dialog_wear_material3.xml b/core/res/res/layout-watch-v36/alert_dialog_wear_material3.xml
deleted file mode 100644
index 8f75456..0000000
--- a/core/res/res/layout-watch-v36/alert_dialog_wear_material3.xml
+++ /dev/null
@@ -1,113 +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.
-  -->
-
-<!-- This layout is the AlertDialog template. It overrides the system layout with the same name.
-    Make sure to include all the existing id of the overridden alert_dialog_material.-->
-<com.android.internal.widget.WatchListDecorLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/parentPanel"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent">
-    <ScrollView
-        android:id="@+id/scrollView"
-        android:fillViewport="true"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent">
-        <LinearLayout
-            android:orientation="vertical"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content">
-            <!-- Top Panel -->
-            <FrameLayout
-                android:paddingLeft="?dialogPreferredPadding"
-                android:paddingRight="?dialogPreferredPadding"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:id="@+id/topPanel"
-                android:minHeight="@dimen/dialog_list_padding_top_no_title">
-                <include android:id="@+id/title_template"
-                         android:layout_width="match_parent"
-                         android:layout_height="wrap_content"
-                         layout="@layout/alert_dialog_title_material"/>
-            </FrameLayout>
-
-            <!-- Content Panel -->
-            <FrameLayout android:id="@+id/contentPanel"
-                         android:layout_width="match_parent"
-                         android:layout_height="wrap_content"
-                         android:clipToPadding="false">
-                <TextView android:id="@+id/message"
-                          android:layout_width="match_parent"
-                          android:layout_height="wrap_content"
-                          android:gravity="center_horizontal|top"
-                          android:textAppearance="@style/TextAppearance.DeviceDefault.Body1"
-                          android:paddingStart="?dialogPreferredPadding"
-                          android:paddingEnd="?dialogPreferredPadding"
-                          android:paddingTop="8dip"
-                          android:paddingBottom="8dip"/>
-            </FrameLayout>
-
-            <!-- Custom Panel, to replace content panel if needed -->
-            <FrameLayout android:id="@+id/customPanel"
-                         android:layout_width="match_parent"
-                         android:layout_height="match_parent"
-                         android:minHeight="64dp">
-                <FrameLayout android:id="@+android:id/custom"
-                             android:layout_width="match_parent"
-                             android:layout_height="wrap_content" />
-            </FrameLayout>
-
-            <!-- Button Panel -->
-            <FrameLayout
-                android:id="@+id/buttonPanel"
-                android:minHeight="@dimen/dialog_list_padding_bottom_no_buttons"
-                android:layout_weight="1"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:layout_gravity="center">
-                <LinearLayout
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:layout_gravity="bottom"
-                    android:orientation="vertical"
-                    android:paddingBottom="?dialogPreferredPadding"
-                    android:measureWithLargestChild="true">
-                    <Button android:id="@+id/button2"
-                            android:layout_width="wrap_content"
-                            android:layout_height="wrap_content"
-                            android:layout_gravity="center"
-                            android:gravity="center"
-                            android:layout_weight="1"
-                            style="@*android:style/Widget.DeviceDefault.Button.WearMaterial3"/>
-                    <Button android:id="@+id/button3"
-                            android:layout_width="wrap_content"
-                            android:layout_height="wrap_content"
-                            android:layout_gravity="center"
-                            android:gravity="center"
-                            android:layout_weight="1"
-                            style="?android:attr/buttonBarButtonStyle"/>
-                    <Button android:id="@+id/button1"
-                            android:layout_width="match_parent"
-                            android:layout_height="match_parent"
-                            android:layout_gravity="center"
-                            android:gravity="center"
-                            android:layout_weight="1"
-                            style="@*android:style/Widget.DeviceDefault.Button.Filled"/>
-                </LinearLayout>
-            </FrameLayout>
-        </LinearLayout>
-    </ScrollView>
-</com.android.internal.widget.WatchListDecorLayout>
diff --git a/core/res/res/layout-watch-v36/alert_dialog_icon_button_wear_material3.xml b/core/res/res/layout/alert_dialog_icon_button_watch.xml
similarity index 98%
rename from core/res/res/layout-watch-v36/alert_dialog_icon_button_wear_material3.xml
rename to core/res/res/layout/alert_dialog_icon_button_watch.xml
index 407ec7a..e5bc86c 100644
--- a/core/res/res/layout-watch-v36/alert_dialog_icon_button_wear_material3.xml
+++ b/core/res/res/layout/alert_dialog_icon_button_watch.xml
@@ -114,7 +114,7 @@
                         <ImageView
                             android:layout_width="match_parent"
                             android:layout_height="match_parent"
-                            android:src="@drawable/dialog_alert_button_positive"/>
+                            android:src="@drawable/dialog_alert_button_positive_watch"/>
                     </FrameLayout>
                 </LinearLayout>
             </FrameLayout>
diff --git a/core/res/res/layout/alert_dialog_title_watch.xml b/core/res/res/layout/alert_dialog_title_watch.xml
new file mode 100644
index 0000000..9c5148f
--- /dev/null
+++ b/core/res/res/layout/alert_dialog_title_watch.xml
@@ -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.
+  -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical"
+    android:gravity="top|center_horizontal">
+
+    <FrameLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:adjustViewBounds="true">
+
+        <ImageView
+            android:id="@+id/icon"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_horizontal"
+            android:layout_marginBottom="8dp"
+            android:maxHeight="32dp"
+            android:maxWidth="32dp"
+            android:src="@null" />
+    </FrameLayout>
+
+    <TextView
+        android:id="@+id/alertTitle"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="@dimen/alertDialog_material_side_margin_title"
+        android:layout_marginEnd="@dimen/alertDialog_material_side_margin_title"
+        android:textAppearance="@style/TextAppearance.AlertDialog.Title"
+        android:gravity="center" />
+</LinearLayout>
\ No newline at end of file
diff --git a/core/res/res/layout/alert_dialog_watch.xml b/core/res/res/layout/alert_dialog_watch.xml
new file mode 100644
index 0000000..cb81fc5
--- /dev/null
+++ b/core/res/res/layout/alert_dialog_watch.xml
@@ -0,0 +1,138 @@
+<!--
+  ~ Copyright (C) 2024 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<!-- This layout is the AlertDialog template. It overrides the system layout with the same name.
+    Make sure to include all the existing id of the overridden alert_dialog_material.-->
+<com.android.internal.widget.WatchListDecorLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/parentPanel"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <ScrollView
+        android:id="@+id/scrollView"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:fillViewport="true">
+
+        <requestFocus />
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="vertical"
+            android:layout_marginStart="@dimen/alertDialog_material_side_margin"
+            android:layout_marginEnd="@dimen/alertDialog_material_side_margin"
+            android:gravity="center_vertical">
+
+            <!-- Top Spacer -->
+            <View
+                android:layout_width="match_parent"
+                android:layout_height="@dimen/alertDialog_material_top_margin" />
+
+            <!-- Top Panel -->
+            <FrameLayout
+                android:id="@+id/topPanel"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:minHeight="@dimen/dialog_list_padding_top_no_title">
+
+                <include
+                    android:id="@+id/title_template"
+                    layout="@layout/alert_dialog_title_watch"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content" />
+            </FrameLayout>
+
+            <!-- Content Panel -->
+            <FrameLayout
+                android:id="@+id/contentPanel"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:paddingTop="12dp">
+
+                <TextView
+                    android:id="@+id/message"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:layout_marginStart="@dimen/alertDialog_material_side_margin_body"
+                    android:layout_marginEnd="@dimen/alertDialog_material_side_margin_body"
+                    android:textAppearance="@style/TextAppearance.AlertDialog.Body1"
+                    android:gravity="center_horizontal|top" />
+            </FrameLayout>
+
+            <!-- Custom Panel, to replace content panel if needed -->
+            <FrameLayout
+                android:id="@+id/customPanel"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:minHeight="64dp">
+
+                <FrameLayout
+                    android:id="@+android:id/custom"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content" />
+            </FrameLayout>
+
+            <!-- Button Panel -->
+            <FrameLayout
+                android:id="@+id/buttonPanel"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center"
+                android:minHeight="@dimen/dialog_list_padding_bottom_no_buttons">
+
+                <LinearLayout
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_marginTop="16dp"
+                    android:layout_gravity="bottom"
+                    android:orientation="vertical">
+                    <!-- Positive Button -->
+                    <Button
+                        android:id="@+id/button1"
+                        style="@*android:style/Widget.DeviceDefault.Button.Filled"
+                        android:layout_width="match_parent"
+                        android:layout_height="match_parent"
+                        android:layout_gravity="center"
+                        android:gravity="center" />
+                    <!--Neutral Button -->
+                    <Button
+                        android:id="@+id/button3"
+                        style="@*android:style/Widget.DeviceDefault.Button.WearMaterial3"
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:layout_gravity="center"
+                        android:gravity="center" />
+                    <!-- Negative Button -->
+                    <Button
+                        android:id="@+id/button2"
+                        style="@*android:style/Widget.DeviceDefault.Button.WearMaterial3"
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:layout_marginTop="4dp"
+                        android:layout_gravity="center"
+                        android:gravity="center" />
+                </LinearLayout>
+            </FrameLayout>
+
+            <!-- Bottom Spacer -->
+            <View
+                android:layout_width="match_parent"
+                android:layout_height="@dimen/alertDialog_material_bottom_margin" />
+
+        </LinearLayout>
+    </ScrollView>
+</com.android.internal.widget.WatchListDecorLayout>
diff --git a/core/res/res/layout/notification_2025_messaging_group.xml b/core/res/res/layout/notification_2025_messaging_group.xml
index c1b491f..ecaf0b9 100644
--- a/core/res/res/layout/notification_2025_messaging_group.xml
+++ b/core/res/res/layout/notification_2025_messaging_group.xml
@@ -24,13 +24,13 @@
     android:orientation="horizontal" >
     <FrameLayout
         android:id="@+id/message_icon_container"
-        android:layout_width="@dimen/conversation_content_start"
+        android:layout_width="@dimen/notification_2025_content_margin_start"
         android:layout_height="wrap_content">
         <ImageView
             android:layout_gravity="top|center_horizontal"
             android:id="@+id/message_icon"
-            android:layout_width="@dimen/messaging_avatar_size"
-            android:layout_height="@dimen/messaging_avatar_size"
+            android:layout_width="@dimen/notification_2025_icon_circle_size"
+            android:layout_height="@dimen/notification_2025_icon_circle_size"
             android:background="@drawable/notification_icon_circle"
             android:clipToOutline="true"
             android:scaleType="centerCrop"
@@ -58,14 +58,14 @@
     </com.android.internal.widget.RemeasuringLinearLayout>
     <FrameLayout
         android:id="@+id/messaging_group_icon_container"
-        android:layout_width="@dimen/messaging_avatar_size"
-        android:layout_height="@dimen/messaging_avatar_size"
+        android:layout_width="@dimen/notification_2025_icon_circle_size"
+        android:layout_height="@dimen/notification_2025_icon_circle_size"
         android:layout_marginStart="12dp"
         android:visibility="gone"/>
     <FrameLayout
         android:id="@+id/messaging_group_sending_progress_container"
         android:layout_width="@dimen/messaging_group_sending_progress_size"
-        android:layout_height="@dimen/messaging_avatar_size"
+        android:layout_height="@dimen/notification_2025_icon_circle_size"
         android:layout_marginStart="12dp"
         android:layout_gravity="top"
         android:visibility="gone">
diff --git a/core/res/res/layout/notification_2025_template_expanded_messaging.xml b/core/res/res/layout/notification_2025_template_expanded_messaging.xml
index 5b58726..e3c2014 100644
--- a/core/res/res/layout/notification_2025_template_expanded_messaging.xml
+++ b/core/res/res/layout/notification_2025_template_expanded_messaging.xml
@@ -33,7 +33,7 @@
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:layout_gravity="top"
-            android:layout_marginTop="@dimen/notification_content_margin_top"
+            android:layout_marginTop="@dimen/notification_2025_header_height"
             android:clipChildren="false"
             android:orientation="vertical">
 
diff --git a/core/res/res/layout/notification_2025_template_header.xml b/core/res/res/layout/notification_2025_template_header.xml
index 63872af..fc727e1 100644
--- a/core/res/res/layout/notification_2025_template_header.xml
+++ b/core/res/res/layout/notification_2025_template_header.xml
@@ -20,7 +20,6 @@
     android:id="@+id/notification_header"
     android:layout_width="match_parent"
     android:layout_height="@dimen/notification_2025_header_height"
-    android:layout_marginBottom="@dimen/notification_header_margin_bottom"
     android:clipChildren="false"
     android:gravity="center_vertical"
     android:orientation="horizontal"
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index c02c13c..4d96ade 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -371,25 +371,25 @@
     <string name="permlab_statusBarService" msgid="2523421018081437981">"wees die statusbalk"</string>
     <string name="permdesc_statusBarService" msgid="6652917399085712557">"Laat die app toe om die statusbalk te wees."</string>
     <string name="permlab_expandStatusBar" msgid="1184232794782141698">"vou statusbalk in of uit"</string>
-    <string name="permdesc_expandStatusBar" msgid="7180756900448498536">"Laat die program toe om die statusbalk uit te vou of in te vou."</string>
+    <string name="permdesc_expandStatusBar" msgid="7180756900448498536">"Laat die app toe om die statusbalk uit te vou of in te vou."</string>
     <string name="permlab_fullScreenIntent" msgid="4310888199502509104">"wys kennisgewings as volskermaktiwiteite op \'n geslote skerm"</string>
-    <string name="permdesc_fullScreenIntent" msgid="1100721419406643997">"Laat die program toe om kennisgewings as volskermaktiwiteite op \'n geslote toestel te wys"</string>
+    <string name="permdesc_fullScreenIntent" msgid="1100721419406643997">"Laat die app toe om kennisgewings as volskermaktiwiteite op \'n geslote toestel te wys"</string>
     <string name="permlab_install_shortcut" msgid="7451554307502256221">"installeer kortpaaie"</string>
     <string name="permdesc_install_shortcut" msgid="4476328467240212503">"Stel \'n app in staat om Tuisskerm-kortpaaie by te voeg sonder gebruikerinmenging."</string>
     <string name="permlab_uninstall_shortcut" msgid="295263654781900390">"deïnstalleer kortpaaie"</string>
     <string name="permdesc_uninstall_shortcut" msgid="1924735350988629188">"Laat die app toe om Tuisskerm-kortpaaie te verwyder sonder gebruikerinmenging."</string>
     <string name="permlab_processOutgoingCalls" msgid="4075056020714266558">"herlei uitgaande oproepe"</string>
-    <string name="permdesc_processOutgoingCalls" msgid="7833149750590606334">"Laat die program toe om te sien watter nommer tydens \'n uitgaande oproep geskakel word, met die opsie om die oproep na \'n ander nommer te herlei of die oproep heeltemal te beëindig."</string>
+    <string name="permdesc_processOutgoingCalls" msgid="7833149750590606334">"Laat die app toe om te sien watter nommer tydens \'n uitgaande oproep geskakel word, met die opsie om die oproep na \'n ander nommer te herlei of die oproep heeltemal te beëindig."</string>
     <string name="permlab_answerPhoneCalls" msgid="4131324833663725855">"antwoord foonoproepe"</string>
-    <string name="permdesc_answerPhoneCalls" msgid="894386681983116838">"Laat die program toe om inkomende foonoproepe te antwoord."</string>
+    <string name="permdesc_answerPhoneCalls" msgid="894386681983116838">"Laat die app toe om inkomende foonoproepe te antwoord."</string>
     <string name="permlab_receiveSms" msgid="505961632050451881">"ontvang teksboodskappe (SMS)"</string>
     <string name="permdesc_receiveSms" msgid="1797345626687832285">"Laat die app toe om SMS-boodskappe te ontvang en te verwerk. Dit beteken dat die app boodskappe wat na jou toestel gestuur is, kan monitor of uitvee, sonder dat jy dit gesien het."</string>
     <string name="permlab_receiveMms" msgid="4000650116674380275">"ontvang teksboodskappe (MMS)"</string>
-    <string name="permdesc_receiveMms" msgid="958102423732219710">"Laat die program toe om MMS-boodskappe te ontvang en te verwerk. Dit beteken dat die program boodskappe wat na jou toestel gestuur is kan monitor of uitvee, sonder dat jy dit gesien het."</string>
+    <string name="permdesc_receiveMms" msgid="958102423732219710">"Laat die app toe om MMS-boodskappe te ontvang en te verwerk. Dit beteken dat die app boodskappe wat na jou toestel gestuur is kan monitor of uitvee, sonder dat jy dit gesien het."</string>
     <string name="permlab_bindCellBroadcastService" msgid="586746677002040651">"Stuur seluitsendingboodskappe aan"</string>
     <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"Laat die app toe om aan die seluitsendingmodule te bind om seluitsendingboodskappe aan te stuur wanneer hulle ontvang word. Seluitsendingwaarskuwings word in sommige liggings gelewer om jou oor noodsituasies te waarsku. Kwaadwillige apps kan met die werkverrigting of werking van jou toestel inmeng wanneer \'n noodseluitsending ontvang word."</string>
     <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"Bestuur voortgaande oproepe"</string>
-    <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"Stel \'n program in staat om besonderhede oor voortgaande oproepe op jou toestel te sien, en hierdie oproepe te beheer."</string>
+    <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"Stel \'n app in staat om besonderhede oor voortgaande oproepe op jou toestel te sien, en hierdie oproepe te beheer."</string>
     <string name="permlab_accessLastKnownCellId" msgid="7638226620825665130">"Kry toegang tot laaste bekende selidentiteit."</string>
     <string name="permdesc_accessLastKnownCellId" msgid="6664621339249308857">"Laat ’n app toe om toegang tot die laaste bekende selidentiteit te kry wat deur telefonie verskaf is"</string>
     <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"lees seluitsending-boodskappe"</string>
@@ -399,17 +399,17 @@
     <string name="permlab_sendSms" msgid="7757368721742014252">"SMS-boodskappe te stuur en te bekyk"</string>
     <string name="permdesc_sendSms" msgid="6757089798435130769">"Laat die app toe om SMS-boodskappe te stuur. Dit kan tot onverwagse heffings lei. Kwaadwillige apps kan jou geld kos deur boodskappe sonder jou bevestiging te stuur."</string>
     <string name="permlab_readSms" msgid="5164176626258800297">"lees jou teksboodskappe (SMS of MMS)"</string>
-    <string name="permdesc_readSms" product="tablet" msgid="7912990447198112829">"Hierdie program kan alle SMS\'e (teksboodskappe) wat op jou tablet geberg is, lees."</string>
+    <string name="permdesc_readSms" product="tablet" msgid="7912990447198112829">"Hierdie app kan alle SMS\'e (teksboodskappe) wat op jou tablet geberg is, lees."</string>
     <string name="permdesc_readSms" product="tv" msgid="3054753345758011986">"Hierdie app kan alle SMS- (teks)-boodskappe lees wat op jou Android TV-toestel geberg is."</string>
-    <string name="permdesc_readSms" product="default" msgid="774753371111699782">"Hierdie program kan alle SMS\'e (teksboodskappe) wat op jou foon geberg is, lees."</string>
+    <string name="permdesc_readSms" product="default" msgid="774753371111699782">"Hierdie app kan alle SMS\'e (teksboodskappe) wat op jou foon geberg is, lees."</string>
     <string name="permlab_receiveWapPush" msgid="4223747702856929056">"ontvang teksboodskappe (WAP)"</string>
     <string name="permdesc_receiveWapPush" msgid="1638677888301778457">"Laat die app toe om WAP-boodskappe te ontvang en te verwerk. Hierdie toestemming sluit ook in dat boodskappe wat na jou toestel gestuur is, gemonitor of uitgevee kan word, sonder dat jy dit gesien het."</string>
     <string name="permlab_getTasks" msgid="7460048811831750262">"haal lopende programme op"</string>
-    <string name="permdesc_getTasks" msgid="7388138607018233726">"Laat die program toe om inligting oor die huidig- en onlangslopende take op te haal. Dit kan moontlik die program toelaat om inligting oor watter programme op die toestel gebruik word, te ontdek."</string>
+    <string name="permdesc_getTasks" msgid="7388138607018233726">"Laat die app toe om inligting oor die huidig- en onlangslopende take op te haal. Dit kan moontlik die app toelaat om inligting oor watter apps op die toestel gebruik word, te ontdek."</string>
     <string name="permlab_manageProfileAndDeviceOwners" msgid="639849495253987493">"bestuur profiel- en toesteleienaars"</string>
     <string name="permdesc_manageProfileAndDeviceOwners" msgid="7304240671781989283">"Laat programme toe om die profieleienaars en die toesteleienaar te stel."</string>
     <string name="permlab_reorderTasks" msgid="7598562301992923804">"herrangskik lopende programme"</string>
-    <string name="permdesc_reorderTasks" msgid="8796089937352344183">"Laat die program toe om take na die voorgrond of agtergrond te skuif. Die program kan dit moontlik sonder jou insette doen."</string>
+    <string name="permdesc_reorderTasks" msgid="8796089937352344183">"Laat die app toe om take na die voorgrond of agtergrond te skuif. Die app kan dit moontlik sonder jou insette doen."</string>
     <string name="permlab_enableCarMode" msgid="893019409519325311">"aktiveer motormodus"</string>
     <string name="permdesc_enableCarMode" msgid="56419168820473508">"Laat die app toe om die motormodus te aktiveer."</string>
     <string name="permlab_killBackgroundProcesses" msgid="6559320515561928348">"maak ander programme toe"</string>
@@ -431,7 +431,7 @@
     <string name="permdesc_persistentActivity" product="tv" msgid="6800526387664131321">"Laat die app toe om dele daarvan in die geheue te laat voortbestaan. Dit kan geheue wat vir ander apps beskikbaar is, beperk en sodoende jou Android TV-toestel stadiger maak."</string>
     <string name="permdesc_persistentActivity" product="default" msgid="1914841924366562051">"Laat die app toe om dele van die app deurdringend in die geheue te hou. Dit kan geheue wat aan ander apps beskikbaar is, beperk, en die foon stadiger maak."</string>
     <string name="permlab_foregroundService" msgid="1768855976818467491">"laat loop voorgronddiens"</string>
-    <string name="permdesc_foregroundService" msgid="8720071450020922795">"Laat die program toe om van voorgronddienste gebruik te maak."</string>
+    <string name="permdesc_foregroundService" msgid="8720071450020922795">"Laat die app toe om van voorgronddienste gebruik te maak."</string>
     <string name="permlab_foregroundServiceCamera" msgid="7814751737955715297">"gebruik voorgronddienstipe “kamera”"</string>
     <string name="permdesc_foregroundServiceCamera" msgid="6973701931250595727">"Laat die app toe om die voorgronddienstipe “kamera” te gebruik"</string>
     <string name="permlab_foregroundServiceConnectedDevice" msgid="3019650546176872501">"gebruik voorgronddienstipe “gekoppelde toestel”"</string>
@@ -461,27 +461,27 @@
     <string name="permlab_foregroundServiceSpecialUse" msgid="7973536745876645082">"gebruik voorgronddienstipe “spesiale gebruik”"</string>
     <string name="permdesc_foregroundServiceSpecialUse" msgid="646713654541885919">"Laat die app toe om die voorgronddienstipe “spesiale gebruik” te gebruik"</string>
     <string name="permlab_getPackageSize" msgid="375391550792886641">"meet programberging-ruimte"</string>
-    <string name="permdesc_getPackageSize" msgid="742743530909966782">"Laat die program toe om sy kode, data en kasgroottes op te haal"</string>
+    <string name="permdesc_getPackageSize" msgid="742743530909966782">"Laat die app toe om sy kode, data en kasgroottes op te haal"</string>
     <string name="permlab_writeSettings" msgid="8057285063719277394">"verander stelsel-instellings"</string>
-    <string name="permdesc_writeSettings" msgid="8293047411196067188">"Laat die program toe om die stelsel se instellingsdata te verander. Kwaadwillige programme kan dalk jou stelsel se opstelling korrupteer."</string>
+    <string name="permdesc_writeSettings" msgid="8293047411196067188">"Laat die app toe om die stelsel se instellingsdata te verander. Kwaadwillige apps kan dalk jou stelsel se opstelling korrupteer."</string>
     <string name="permlab_receiveBootCompleted" msgid="6643339400247325379">"laat loop wanneer begin"</string>
     <string name="permdesc_receiveBootCompleted" product="tablet" msgid="5565659082718177484">"Laat die app toe om self te begin sodra die stelsel geselflaai het. Dit maak dat dit langer neem vir die tablet om te begin, en dit laat die foon toe om die tablet stadiger te maak omdat dit altyd loop."</string>
     <string name="permdesc_receiveBootCompleted" product="tv" msgid="4900842256047614307">"Laat die app toe om self te begin sodra die stelsel klaar geselflaai het. Dit kan dalk daartoe lei dat die toestel langer neem om jou Android TV-toestel te begin, en laat die app toe om die hele toestel stadiger te maak deur altyd te loop."</string>
-    <string name="permdesc_receiveBootCompleted" product="default" msgid="7912677044558690092">"Laat die program toe om homself te begin so gou as moontlik nadat die stelsel laai. Dit maak dat dit langer neem vir die foon om te begin, en dit laat die foon toe om die foon stadiger te maak omdat dit altyd loop."</string>
+    <string name="permdesc_receiveBootCompleted" product="default" msgid="7912677044558690092">"Laat die app toe om homself te begin so gou as moontlik nadat die stelsel laai. Dit maak dat dit langer neem vir die foon om te begin, en dit laat die foon toe om die foon stadiger te maak omdat dit altyd loop."</string>
     <string name="permlab_broadcastSticky" msgid="4552241916400572230">"Stuur klewerige uitsending"</string>
     <string name="permdesc_broadcastSticky" product="tablet" msgid="5058486069846384013">"Laat die app toe om vaste uitsendings te stuur, wat agterbly nadat die uitsending klaar is. Oormatige gebruik kan die tablet stadig of onstabiel maak deurdat dit te veel geheue gebruik."</string>
     <string name="permdesc_broadcastSticky" product="tv" msgid="2338185920171000650">"Laat die app toe om vaste uitsendings wat agterbly nadat die uitsending eindig, te stuur. Oormatige gebruik kan jou Android TV-toestel stadig of onstabiel maak omdat dit veroorsaak dat jou toestel te veel geheue gebruik."</string>
     <string name="permdesc_broadcastSticky" product="default" msgid="134529339678913453">"Laat die app toe om vaste uitsendings te stuur, wat agterbly nadat die uitsending klaar is. Oormatige gebruik kan die foon stadig of onstabiel maak deurdat dit te veel geheue gebruik."</string>
     <string name="permlab_readContacts" msgid="8776395111787429099">"lees jou kontakte"</string>
-    <string name="permdesc_readContacts" product="tablet" msgid="6430093481659992692">"Laat die program toe om data te lees oor jou kontakte wat op jou tablet geberg is. Programme sal ook toegang hê tot die rekeninge op jou tablet wat kontakte geskep het. Dit kan rekeninge insluit wat geskep is deur programme wat jy geïnstalleer het. Hierdie toestemming laat programme toe om jou kontakdata te stoor, en kwaadwillige programme kan kontakdata deel sonder dat jy dit weet."</string>
-    <string name="permdesc_readContacts" product="tv" msgid="8400138591135554789">"Laat die program toe om data te lees oor jou kontakte wat op jou Android TV-toestel geberg is. Programme sal ook toegang hê tot die rekeninge op jou Android TV-toestel wat kontakte geskep het. Dit kan rekeninge insluit wat geskep is deur programme wat jy geïnstalleer het. Hierdie toestemming laat programme toe om jou kontakdata te stoor, en kwaadwillige programme kan kontakdata deel sonder dat jy dit weet."</string>
-    <string name="permdesc_readContacts" product="default" msgid="4911989776203207644">"Laat die program toe om data te lees oor jou kontakte wat op jou foon geberg is. Programme sal ook toegang hê tot die rekeninge op jou foon wat kontakte geskep het. Dit kan rekeninge insluit wat geskep is deur programme wat jy geïnstalleer het. Hierdie toestemming laat programme toe om jou kontakdata te stoor, en kwaadwillige programme kan kontakdata deel sonder dat jy dit weet."</string>
+    <string name="permdesc_readContacts" product="tablet" msgid="6430093481659992692">"Laat die app toe om data te lees oor jou kontakte wat op jou tablet geberg is. Apps sal ook toegang hê tot die rekeninge op jou tablet wat kontakte geskep het. Dit kan rekeninge insluit wat geskep is deur apps wat jy geïnstalleer het. Hierdie toestemming laat apps toe om jou kontakdata te stoor, en kwaadwillige apps kan kontakdata deel sonder dat jy dit weet."</string>
+    <string name="permdesc_readContacts" product="tv" msgid="8400138591135554789">"Laat die app toe om data te lees oor jou kontakte wat op jou Android TV-toestel geberg is. Apps sal ook toegang hê tot die rekeninge op jou Android TV-toestel wat kontakte geskep het. Dit kan rekeninge insluit wat geskep is deur apps wat jy geïnstalleer het. Hierdie toestemming laat apps toe om jou kontakdata te stoor, en kwaadwillige apps kan kontakdata deel sonder dat jy dit weet."</string>
+    <string name="permdesc_readContacts" product="default" msgid="4911989776203207644">"Laat die app toe om data te lees oor jou kontakte wat op jou foon geberg is. Apps sal ook toegang hê tot die rekeninge op jou foon wat kontakte geskep het. Dit kan rekeninge insluit wat geskep is deur apps wat jy geïnstalleer het. Hierdie toestemming laat apps toe om jou kontakdata te stoor, en kwaadwillige apps kan kontakdata deel sonder dat jy dit weet."</string>
     <string name="permlab_writeContacts" msgid="8919430536404830430">"verander jou kontakte"</string>
-    <string name="permdesc_writeContacts" product="tablet" msgid="6422419281427826181">"Laat die program toe om die data te wysig oor jou kontakte wat op jou tablet geberg is. Hierdie toestemming laat programme toe om kontakdata uit te vee."</string>
-    <string name="permdesc_writeContacts" product="tv" msgid="6488872735379978935">"Laat die program toe om die data te wysig oor jou kontakte wat op jou Android TV-toestel geberg is. Hierdie toestemming laat programme toe om kontakdata uit te vee."</string>
-    <string name="permdesc_writeContacts" product="default" msgid="8304795696237065281">"Laat die program toe om die data te wysig oor jou kontakte wat op jou foon geberg is. Hierdie toestemming laat programme toe om kontakdata uit te vee."</string>
+    <string name="permdesc_writeContacts" product="tablet" msgid="6422419281427826181">"Laat die app toe om die data te wysig oor jou kontakte wat op jou tablet geberg is. Hierdie toestemming laat apps toe om kontakdata uit te vee."</string>
+    <string name="permdesc_writeContacts" product="tv" msgid="6488872735379978935">"Laat die app toe om die data te wysig oor jou kontakte wat op jou Android TV-toestel geberg is. Hierdie toestemming laat apps toe om kontakdata uit te vee."</string>
+    <string name="permdesc_writeContacts" product="default" msgid="8304795696237065281">"Laat die app toe om die data te wysig oor jou kontakte wat op jou foon geberg is. Hierdie toestemming laat apps toe om kontakdata uit te vee."</string>
     <string name="permlab_readCallLog" msgid="1739990210293505948">"lees oproeprekord"</string>
-    <string name="permdesc_readCallLog" msgid="8964770895425873433">"Hierdie program kan jou oproepgeskiedenis lees."</string>
+    <string name="permdesc_readCallLog" msgid="8964770895425873433">"Hierdie app kan jou oproepgeskiedenis lees."</string>
     <string name="permlab_writeCallLog" msgid="670292975137658895">"skryf oproeprekord"</string>
     <string name="permdesc_writeCallLog" product="tablet" msgid="2657525794731690397">"Laat die app toe om jou tablet se oproeprekord, insluitende data oor inkomende en uitgaande oproepe, te verander. Kwaadwillige apps kan dit gebruik om jou oproeprekord uit te vee of te verander."</string>
     <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"Laat die app toe om jou Android TV-toestel se oproeprekord te wysig, insluitend data oor inkomende en uitgaande oproepe. Kwaadwillige apps kan dit gebruik om jou oproeprekord uit te vee of te wysig."</string>
@@ -493,74 +493,74 @@
     <string name="permlab_readCalendar" msgid="6408654259475396200">"Lees kalendergebeurtenisse en -besonderhede"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"Hierdie app kan alle kalendergebeurtenisse lees wat op jou tablet geberg is of jou kalenderdata stoor."</string>
     <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"Hierdie app kan alle kalendergeleenthede lees wat op jou Android TV-toestel geberg is of jou kalenderdata stoor."</string>
-    <string name="permdesc_readCalendar" product="default" msgid="9118823807655829957">"Hierdie program kan alle kalendergebeurtenisse lees wat op jou foon geberg is of jou kalenderdata stoor."</string>
+    <string name="permdesc_readCalendar" product="default" msgid="9118823807655829957">"Hierdie app kan alle kalendergebeurtenisse lees wat op jou foon geberg is of jou kalenderdata stoor."</string>
     <string name="permlab_writeCalendar" msgid="6422137308329578076">"voeg by of verander kalenderafsprake en stuur \'n e-pos aan gaste sonder eienaars se medewete"</string>
-    <string name="permdesc_writeCalendar" product="tablet" msgid="8722230940717092850">"Hierdie program kan kalendergebeurtenisse op jou tablet byvoeg, verwyder of verander. Hierdie program kan boodskappe stuur wat lyk of dit van kalendereienaars af kom of gebeurtenisse verander sonder om hul eienaars in te lig."</string>
-    <string name="permdesc_writeCalendar" product="tv" msgid="951246749004952706">"Hierdie program kan kalendergeleenthede op jou Android TV-toestel byvoeg, verwyder of verander. Hierdie program kan boodskappe stuur wat lyk of dit van kalendereienaars af kom of geleenthede verander sonder om hul eienaars in kennis te stel."</string>
+    <string name="permdesc_writeCalendar" product="tablet" msgid="8722230940717092850">"Hierdie app kan kalendergebeurtenisse op jou tablet byvoeg, verwyder of verander. Hierdie app kan boodskappe stuur wat lyk of dit van kalendereienaars af kom of gebeurtenisse verander sonder om hul eienaars in te lig."</string>
+    <string name="permdesc_writeCalendar" product="tv" msgid="951246749004952706">"Hierdie app kan kalendergeleenthede op jou Android TV-toestel byvoeg, verwyder of verander. Hierdie app kan boodskappe stuur wat lyk of dit van kalendereienaars af kom of geleenthede verander sonder om hul eienaars in kennis te stel."</string>
     <string name="permdesc_writeCalendar" product="default" msgid="5416380074475634233">"Hierdie app kan kalendergebeurtenisse op jou foon byvoeg, verwyder of verander. Hierdie app kan boodskappe stuur wat lyk of dit van kalendereienaars af kom of gebeurtenisse verander sonder om hul eienaars in te lig."</string>
     <string name="permlab_accessLocationExtraCommands" msgid="5162339812057983988">"Kry toegang tot ekstra liggingverskaffer-bevele"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="355369611979907967">"Gee die app toegang tot ekstra liggingverskaffer-bevele. Dit kan die app dalk toelaat om in te meng met die werking van die GPS of ander liggingbronne."</string>
     <string name="permlab_accessFineLocation" msgid="6426318438195622966">"kry net op die voorgrond toegang tot presiese ligging"</string>
-    <string name="permdesc_accessFineLocation" msgid="6732174080240016335">"Hierdie program kan jou presiese ligging van liggingdienste af kry terwyl die program gebruik word. Liggingdienste vir jou toestel moet aangeskakel wees vir die program om die ligging te kry. Dit kan batterygebruik verhoog."</string>
+    <string name="permdesc_accessFineLocation" msgid="6732174080240016335">"Hierdie app kan jou presiese ligging van liggingdienste af kry terwyl die app gebruik word. Liggingdienste vir jou toestel moet aangeskakel wees vir die app om die ligging te kry. Dit kan batterygebruik verhoog."</string>
     <string name="permlab_accessCoarseLocation" msgid="1561042925407799741">"kry benaderde ligging net op die voorgrond"</string>
-    <string name="permdesc_accessCoarseLocation" msgid="778521847873199160">"Hierdie program kan jou benaderde ligging van liggingdienste af kry terwyl die program gebruik word. Liggingdienste vir jou toestel moet aangeskakel wees vir die program om die ligging te kry."</string>
+    <string name="permdesc_accessCoarseLocation" msgid="778521847873199160">"Hierdie app kan jou benaderde ligging van liggingdienste af kry terwyl die app gebruik word. Liggingdienste vir jou toestel moet aangeskakel wees vir die app om die ligging te kry."</string>
     <string name="permlab_accessBackgroundLocation" msgid="1721164702777366138">"kry ligging op die agtergrond"</string>
-    <string name="permdesc_accessBackgroundLocation" msgid="8264885066095638105">"Hierdie program kan op enige tydstip toegang tot ligging kry, selfs wanneer die program nie gebruik word nie."</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="8264885066095638105">"Hierdie app kan op enige tydstip toegang tot ligging kry, selfs wanneer die app nie gebruik word nie."</string>
     <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"verander jou klankinstellings"</string>
-    <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"Laat die program toe om globale klankinstellings soos volume en watter luidspreker vir uitvoer gebruik word, te verander."</string>
+    <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"Laat die app toe om globale klankinstellings soos volume en watter luidspreker vir uitvoer gebruik word, te verander."</string>
     <string name="permlab_recordAudio" msgid="1208457423054219147">"neem klank op"</string>
-    <string name="permdesc_recordAudio" msgid="5857246765327514062">"Hierdie die program kan oudio met die mikrofoon opneem terwyl die program gebruik word."</string>
+    <string name="permdesc_recordAudio" msgid="5857246765327514062">"Hierdie die app kan oudio met die mikrofoon opneem terwyl die app gebruik word."</string>
     <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"neem oudio op die agtergrond op"</string>
-    <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"Hierdie program kan enige tyd oudio met die mikrofoon opneem."</string>
+    <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"Hierdie app kan enige tyd oudio met die mikrofoon opneem."</string>
     <string name="permlab_detectScreenCapture" msgid="4447042362828799433">"bespeur skermskote van appvensters"</string>
     <string name="permdesc_detectScreenCapture" msgid="3485784917960342284">"Hierdie app sal ingelig word as ’n skermskoot geneem word terwyl die app gebruik word."</string>
     <string name="permlab_sim_communication" msgid="176788115994050692">"stuur bevele na die SIM"</string>
     <string name="permdesc_sim_communication" msgid="4179799296415957960">"Laat die app toe om bevele na die SIM te stuur. Dit is baie gevaarlik."</string>
     <string name="permlab_activityRecognition" msgid="1782303296053990884">"herken fisieke aktiwiteit"</string>
-    <string name="permdesc_activityRecognition" msgid="8667484762991357519">"Hierdie program kan jou fisieke aktiwiteit herken."</string>
+    <string name="permdesc_activityRecognition" msgid="8667484762991357519">"Hierdie app kan jou fisieke aktiwiteit herken."</string>
     <string name="permlab_camera" msgid="6320282492904119413">"neem foto\'s en video\'s"</string>
-    <string name="permdesc_camera" msgid="5240801376168647151">"Hierdie program kan met die kamera foto\'s neem en video\'s opneem terwyl die program gebruik word."</string>
+    <string name="permdesc_camera" msgid="5240801376168647151">"Hierdie app kan met die kamera foto\'s neem en video\'s opneem terwyl die app gebruik word."</string>
     <string name="permlab_backgroundCamera" msgid="7549917926079731681">"neem foto\'s en video\'s op die agtergrond"</string>
-    <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"Hierdie program kan enige tyd met die kamera foto\'s neem en video\'s opneem."</string>
+    <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"Hierdie app kan enige tyd met die kamera foto\'s neem en video\'s opneem."</string>
     <string name="permlab_systemCamera" msgid="3642917457796210580">"Gee \'n app of diens toegang tot stelselkameras om foto\'s en video\'s te neem"</string>
-    <string name="permdesc_systemCamera" msgid="5938360914419175986">"Hierdie bevoorregte of stelselprogram kan enige tyd met \'n stelselkamera foto\'s neem en video\'s opneem. Vereis dat die program ook die android.permission.CAMERA-toestemming het"</string>
-    <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Laat \'n program of diens toe om terugbeloproepe te ontvang oor kameratoestelle wat oopgemaak of toegemaak word."</string>
-    <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"Hierdie program kan terugbeloproepe ontvang wanneer enige kameratoestel oopgemaak (deur watter program) of toegemaak word."</string>
+    <string name="permdesc_systemCamera" msgid="5938360914419175986">"Hierdie bevoorregte of stelselapp kan enige tyd met \'n stelselkamera foto\'s neem en video\'s opneem. Vereis dat die app ook die android.permission.CAMERA-toestemming het"</string>
+    <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Laat \'n app of diens toe om terugbeloproepe te ontvang oor kameratoestelle wat oopgemaak of toegemaak word."</string>
+    <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"Hierdie app kan terugbeloproepe ontvang wanneer enige kameratoestel oopgemaak (deur watter app) of toegemaak word."</string>
     <string name="permlab_cameraHeadlessSystemUser" msgid="680194666834500050">"Laat ’n app of diens toe om toegang tot die kamera te verkry as ’n stelselgebruiker sonder koppelvlak."</string>
     <string name="permdesc_cameraHeadlessSystemUser" msgid="6963163319710996412">"Hierdie app het toegang tot die kamera as ’n stelselgebruiker sonder koppelvlak."</string>
     <string name="permlab_vibrate" msgid="8596800035791962017">"beheer vibrasie"</string>
-    <string name="permdesc_vibrate" msgid="8733343234582083721">"Laat die program toe om die vibrator te beheer."</string>
-    <string name="permdesc_vibrator_state" msgid="7050024956594170724">"Stel die program in staat om toegang tot die vibreerderstand te kry."</string>
+    <string name="permdesc_vibrate" msgid="8733343234582083721">"Laat die app toe om die vibrator te beheer."</string>
+    <string name="permdesc_vibrator_state" msgid="7050024956594170724">"Stel die app in staat om toegang tot die vibreerderstand te kry."</string>
     <string name="permlab_callPhone" msgid="1798582257194643320">"skakel foonnommers direk"</string>
     <string name="permdesc_callPhone" msgid="7892422187827695656">"Laat die app toe om foonnommers sonder jou insae te bel. Dit kan onvoorsiene heffings of oproepe tot gevolg hê. Neem kennis dat dit nie die app toelaat om noodnommers te bel nie. Kwaadwillige apps kan jou geld kos deur oproepe te maak sonder jou bevestiging of diensverskafferkodes te bel wat veroorsaak dat inkomende oproepe outomaties na ’n ander nommer aangestuur word."</string>
     <string name="permlab_accessImsCallService" msgid="442192920714863782">"toegang tot kitsboodskapoproepdiens"</string>
     <string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Laat die app toe om die kitsboodskapdiens te gebruik om oproepe sonder jou ingryping te maak."</string>
     <string name="permlab_readPhoneState" msgid="8138526903259297969">"lees foonstatus en identiteit"</string>
-    <string name="permdesc_readPhoneState" msgid="7229063553502788058">"Laat die program toe om toegang tot die foonfunksies van die toestel te verkry. Hierdie toestemming laat die program toe om te bepaal wat die foonnommer en toestel-IDs is, of die oproep aan die gang is, en die afgeleë nommer wat deur \'n oproep verbind word."</string>
+    <string name="permdesc_readPhoneState" msgid="7229063553502788058">"Laat die app toe om toegang tot die foonfunksies van die toestel te verkry. Hierdie toestemming laat die app toe om te bepaal wat die foonnommer en toestel-IDs is, of die oproep aan die gang is, en die afgeleë nommer wat deur \'n oproep verbind word."</string>
     <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"lees basiese telefoniestatus en -identiteit"</string>
-    <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Gee die program toegang tot die toestel se basiese telefoniekenmerke."</string>
+    <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Gee die app toegang tot die toestel se basiese telefoniekenmerke."</string>
     <string name="permlab_manageOwnCalls" msgid="9033349060307561370">"roeteer oproepe deur die stelsel"</string>
     <string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Laat die app toe om sy oproepe deur die stelsel te stuur om die oproepervaring te verbeter."</string>
     <string name="permlab_callCompanionApp" msgid="3654373653014126884">"sien en beheer oproepe deur die stelsel."</string>
-    <string name="permdesc_callCompanionApp" msgid="8474168926184156261">"Laat die program toe om deurlopende oproepe op die toestel te sien en te beheer. Dit sluit inligting in soos oproepnommers vir oproepe en die toedrag van die oproepe."</string>
+    <string name="permdesc_callCompanionApp" msgid="8474168926184156261">"Laat die app toe om deurlopende oproepe op die toestel te sien en te beheer. Dit sluit inligting in soos oproepnommers vir oproepe en die toedrag van die oproepe."</string>
     <string name="permlab_exemptFromAudioRecordRestrictions" msgid="1164725468350759486">"vrygestel van beperkings op oudio-opnames"</string>
-    <string name="permdesc_exemptFromAudioRecordRestrictions" msgid="2425117015896871976">"Stel die program vry van beperkings om oudio op te neem."</string>
+    <string name="permdesc_exemptFromAudioRecordRestrictions" msgid="2425117015896871976">"Stel die app vry van beperkings om oudio op te neem."</string>
     <string name="permlab_acceptHandover" msgid="2925523073573116523">"gaan voort met \'n oproep uit \'n ander app"</string>
     <string name="permdesc_acceptHandovers" msgid="7129026180128626870">"Laat die app toe om \'n oproep voort te sit wat in \'n ander app begin is."</string>
     <string name="permlab_readPhoneNumbers" msgid="5668704794723365628">"lees foonnommers"</string>
-    <string name="permdesc_readPhoneNumbers" msgid="7368652482818338871">"Laat die program toe om toegang tot die toestel se foonnommers te kry."</string>
+    <string name="permdesc_readPhoneNumbers" msgid="7368652482818338871">"Laat die app toe om toegang tot die toestel se foonnommers te kry."</string>
     <string name="permlab_wakeLock" product="automotive" msgid="1904736682319375676">"hou motorskerm aan"</string>
     <string name="permlab_wakeLock" product="tablet" msgid="1527660973931694000">"verhoed dat tablet slaap"</string>
     <string name="permlab_wakeLock" product="tv" msgid="2856941418123343518">"keer dat jou Android TV-toestel slaap"</string>
     <string name="permlab_wakeLock" product="default" msgid="569409726861695115">"verhoed foon om te slaap"</string>
-    <string name="permdesc_wakeLock" product="automotive" msgid="5995045369683254571">"Laat die program toe om die motorskerm aan te hou."</string>
+    <string name="permdesc_wakeLock" product="automotive" msgid="5995045369683254571">"Laat die app toe om die motorskerm aan te hou."</string>
     <string name="permdesc_wakeLock" product="tablet" msgid="2441742939101526277">"Laat die app toe om die tablet te keer om te slaap."</string>
     <string name="permdesc_wakeLock" product="tv" msgid="2329298966735118796">"Laat die app toe om te keer dat jou Android TV-toestel slaap."</string>
     <string name="permdesc_wakeLock" product="default" msgid="3689523792074007163">"Laat die app toe om die foon te keer om te slaap."</string>
     <string name="permlab_transmitIr" msgid="8077196086358004010">"versend infrarooi"</string>
     <string name="permdesc_transmitIr" product="tablet" msgid="5884738958581810253">"Laat die app toe om die tablet se infrarooisender te gebruik."</string>
     <string name="permdesc_transmitIr" product="tv" msgid="3278506969529173281">"Laat die app toe om jou Android TV-toestel se infrarooisender te gebruik."</string>
-    <string name="permdesc_transmitIr" product="default" msgid="8484193849295581808">"Laat die program toe om die foon se infrarooisender te gebruik."</string>
+    <string name="permdesc_transmitIr" product="default" msgid="8484193849295581808">"Laat die app toe om die foon se infrarooisender te gebruik."</string>
     <string name="permlab_setWallpaper" msgid="6959514622698794511">"stel muurpapier"</string>
     <string name="permdesc_setWallpaper" msgid="2973996714129021397">"Laat die app toe om die stelsel se muurpapier te stel."</string>
     <string name="permlab_accessHiddenProfile" msgid="8607094418491556823">"Kry toegang tot versteekte profiele"</string>
@@ -569,7 +569,7 @@
     <string name="permdesc_setWallpaperHints" msgid="6257053376990044668">"Laat die app toe om die stelsel se muurpapier se groottewenke te stel."</string>
     <string name="permlab_setTimeZone" msgid="7922618798611542432">"stel tydsone"</string>
     <string name="permdesc_setTimeZone" product="tablet" msgid="1788868809638682503">"Laat die app toe om die tablet se tydsone te verander."</string>
-    <string name="permdesc_setTimeZone" product="tv" msgid="9069045914174455938">"Laat die program toe om jou Android TV-toestel se tydsone te verander."</string>
+    <string name="permdesc_setTimeZone" product="tv" msgid="9069045914174455938">"Laat die app toe om jou Android TV-toestel se tydsone te verander."</string>
     <string name="permdesc_setTimeZone" product="default" msgid="4611828585759488256">"Laat die app toe om die foon se tydsone te verander."</string>
     <string name="permlab_getAccounts" msgid="5304317160463582791">"soek rekeninge op die toestel"</string>
     <string name="permdesc_getAccounts" product="tablet" msgid="1784452755887604512">"Laat die app toe om die lys van rekeninge wat aan die tablet bekend is, te kry. Dit kan moontlik enige rekeninge wat geskep is deur apps wat jy geïnstalleer het, insluit."</string>
@@ -578,7 +578,7 @@
     <string name="permlab_accessNetworkState" msgid="2349126720783633918">"bekyk netwerkverbindings"</string>
     <string name="permdesc_accessNetworkState" msgid="4394564702881662849">"Laat die app toe om inligting oor netwerkverbindings, soos watter netwerke bestaan en gekoppel is, te sien."</string>
     <string name="permlab_createNetworkSockets" msgid="3224420491603590541">"verkry volle netwerktoegang"</string>
-    <string name="permdesc_createNetworkSockets" msgid="7722020828749535988">"Laat die program toe om netwerksokke te skep en gepasmaakte netwerkprotokolle te gebruik. Die blaaier en ander programme verskaf reeds die middele waardeur data na die internet gestuur kan word, so hierdie toestemming word nie vereis om data na die internet te stuur nie."</string>
+    <string name="permdesc_createNetworkSockets" msgid="7722020828749535988">"Laat die app toe om netwerksokke te skep en gepasmaakte netwerkprotokolle te gebruik. Die blaaier en ander apps verskaf reeds die middele waardeur data na die internet gestuur kan word, so hierdie toestemming word nie vereis om data na die internet te stuur nie."</string>
     <string name="permlab_changeNetworkState" msgid="8945711637530425586">"verander netwerkverbinding"</string>
     <string name="permdesc_changeNetworkState" msgid="649341947816898736">"Laat die app toe om die status van netwerkkonnektiwiteit te verander."</string>
     <string name="permlab_changeTetherState" msgid="9079611809931863861">"verander verbinde konnektiwiteit"</string>
@@ -586,15 +586,15 @@
     <string name="permlab_accessWifiState" msgid="5552488500317911052">"bekyk Wi-Fi-verbindings"</string>
     <string name="permdesc_accessWifiState" msgid="6913641669259483363">"Laat die app toe om inligting oor wi-fi-netwerke, soos of wi-fi geaktiveer is en die name van gekoppelde wi-fi-toestelle, te sien."</string>
     <string name="permlab_changeWifiState" msgid="7947824109713181554">"koppel en ontkoppel van Wi-Fi"</string>
-    <string name="permdesc_changeWifiState" msgid="7170350070554505384">"Laat die program toe om te koppel aan en te ontkoppel van Wi-Fi-toegangspunte en om veranderings aan Wi-Fi-netwerke se toestelopstellings te maak."</string>
+    <string name="permdesc_changeWifiState" msgid="7170350070554505384">"Laat die app toe om te koppel aan en te ontkoppel van wi-fi-toegangspunte en om veranderings aan wi-fi-netwerke se toestelopstellings te maak."</string>
     <string name="permlab_changeWifiMulticastState" msgid="285626875870754696">"laat Wi-Fi-multisendontvangs toe"</string>
     <string name="permdesc_changeWifiMulticastState" product="tablet" msgid="191079868596433554">"Laat die app toe om pakkies te ontvang wat met behulp van multisaai-adresse na alle toestelle op \'n wi-fi-netwerk gestuur is, nie net jou tablet nie. Dit gebruik meer krag as die nie-multisaaimodus."</string>
     <string name="permdesc_changeWifiMulticastState" product="tv" msgid="1336952358450652595">"Laat die app toe om pakkette te ontvang wat met multi-uitsendingadresse na alle toestelle op \'n wi-fi-netwerk gestuur is, nie net jou Android TV-toestel nie. Dit gebruik meer krag as nie-multi-uitsendingmodus."</string>
-    <string name="permdesc_changeWifiMulticastState" product="default" msgid="8296627590220222740">"Laat die program toe om pakkies te ontvang wat met behulp van multisaai-adresse na alle toestelle op \'n Wi-Fi-netwerk gestuur is, nie net jou foon nie. Dit gebruik meer krag as die nie-multisaaimodus."</string>
+    <string name="permdesc_changeWifiMulticastState" product="default" msgid="8296627590220222740">"Laat die app toe om pakkies te ontvang wat met behulp van multisaai-adresse na alle toestelle op \'n Wi-Fi-netwerk gestuur is, nie net jou foon nie. Dit gebruik meer krag as die nie-multisaaimodus."</string>
     <string name="permlab_bluetoothAdmin" msgid="6490373569441946064">"gaan in by Bluetooth-instellings"</string>
     <string name="permdesc_bluetoothAdmin" product="tablet" msgid="5370837055438574863">"Laat die app toe om die plaaslike Bluetooth-tablet op te stel, en om met afstandbeheer toestelle saam te bind."</string>
     <string name="permdesc_bluetoothAdmin" product="tv" msgid="1623992984547014588">"Laat die app toe om Bluetooth op jou Android TV-toestel op te stel, en om afgeleë toestelle te ontdek en met hulle saam te bind."</string>
-    <string name="permdesc_bluetoothAdmin" product="default" msgid="7381341743021234863">"Laat die program toe om die plaaslike Bluetooth-foon op te stel en te ontdek en met afgeleë toestelle saam te bind."</string>
+    <string name="permdesc_bluetoothAdmin" product="default" msgid="7381341743021234863">"Laat die app toe om die plaaslike Bluetooth-foon op te stel en te ontdek en met toestelle op ’n afstand saam te bind."</string>
     <string name="permlab_accessWimaxState" msgid="7029563339012437434">"koppel aan en ontkoppel van WiMAX"</string>
     <string name="permdesc_accessWimaxState" msgid="5372734776802067708">"Laat die app toe om te bepaal of WiMAX geaktiveer is en of enige WiMAX-netwerke gekoppel is."</string>
     <string name="permlab_changeWimaxState" msgid="6223305780806267462">"verander WiMAX-status"</string>
@@ -603,24 +603,24 @@
     <string name="permdesc_changeWimaxState" product="default" msgid="1551666203780202101">"Laat die app toe om die foon aan WiMAX-netwerke te koppel en daarvan te ontkoppel."</string>
     <string name="permlab_bluetooth" msgid="586333280736937209">"bind saam met Bluetooth-toestelle"</string>
     <string name="permdesc_bluetooth" product="tablet" msgid="3053222571491402635">"Laat die app toe om die opstelling van Bluetooth op die tablet te sien, en om verbindings met saamgebinde toestelle te maak en te aanvaar."</string>
-    <string name="permdesc_bluetooth" product="tv" msgid="8851534496561034998">"Laat die program toe om die opstelling van Bluetooth op jou Android TV-toestel te bekyk, en om verbindings met saamgebinde toestelle te maak en te aanvaar."</string>
+    <string name="permdesc_bluetooth" product="tv" msgid="8851534496561034998">"Laat die app toe om die opstelling van Bluetooth op jou Android TV-toestel te bekyk, en om verbindings met saamgebinde toestelle te maak en te aanvaar."</string>
     <string name="permdesc_bluetooth" product="default" msgid="2779606714091276746">"Laat die app toe om die opstelling van die Bluetooth op die foon te sien, en om verbindings met saamgebinde toestelle te maak en te aanvaar."</string>
     <string name="permlab_bluetooth_scan" msgid="5402587142833124594">"ontdek en bind Bluetooth-toestelle in die omtrek saam"</string>
-    <string name="permdesc_bluetooth_scan" product="default" msgid="6540723536925289276">"Laat die program toe om Bluetooth-toestelle in die omtrek te ontdek en saam te bind"</string>
+    <string name="permdesc_bluetooth_scan" product="default" msgid="6540723536925289276">"Laat die app toe om Bluetooth-toestelle in die omtrek te ontdek en saam te bind"</string>
     <string name="permlab_bluetooth_connect" msgid="6657463246355003528">"koppel aan saamgebinde Bluetooth-toestelle"</string>
-    <string name="permdesc_bluetooth_connect" product="default" msgid="4546016548795544617">"Laat die program toe om aan saamgebinde Bluetooth-toestelle te koppel"</string>
+    <string name="permdesc_bluetooth_connect" product="default" msgid="4546016548795544617">"Laat die app toe om aan saamgebinde Bluetooth-toestelle te koppel"</string>
     <string name="permlab_bluetooth_advertise" msgid="2781147747928853177">"adverteer op Bluetooth-toestelle in die omtrek"</string>
-    <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Laat die program toe om op Bluetooth-toestelle in die omtrek te adverteer"</string>
+    <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Laat die app toe om op Bluetooth-toestelle in die omtrek te adverteer"</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"bepaal relatiewe posisie tussen ultrabreëbandtoestelle in die omtrek"</string>
-    <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Laat die program toe om relatiewe posisie tussen ultrabreëbandtoestelle in die omtrek te bepaal"</string>
+    <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Laat die app toe om relatiewe posisie tussen ultrabreëbandtoestelle in die omtrek te bepaal"</string>
     <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"om interaksie met wi‑fi-toestelle in die omtrek te hê"</string>
-    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Laat die program toe om op toestelle in die omtrek te adverteer, aan hulle te koppel en hul relatiewe posisie te bepaal"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Laat die app toe om op toestelle in die omtrek te adverteer, aan hulle te koppel en hul relatiewe posisie te bepaal"</string>
     <string name="permlab_ranging" msgid="2854543350668593390">"bepaal relatiewe posisie tussen toestelle in die omtrek"</string>
     <string name="permdesc_ranging" msgid="6703905535621521710">"Laat die app toe om relatiewe posisie tussen toestelle in die omtrek te bepaal"</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Voorkeur-NFC-betalingdiensinligting"</string>
-    <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Laat die program toe om voorkeur-NFC-betalingdiensinligting soos geregistreerde hulpmiddels en roetebestemming te kry."</string>
+    <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Laat die app toe om voorkeur-NFC-betalingdiensinligting soos geregistreerde hulpmiddels en roetebestemming te kry."</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"beheer kortveldkommunikasie"</string>
-    <string name="permdesc_nfc" msgid="8352737680695296741">"Laat die program toe om met kortveldkommunikasie- (NFC) merkers, kaarte en lesers te kommunikeer."</string>
+    <string name="permdesc_nfc" msgid="8352737680695296741">"Laat die app toe om met kortveldkommunikasie- (NFC) merkers, kaarte en lesers te kommunikeer."</string>
     <string name="permlab_nfcTransactionEvent" msgid="5868209446710407679">"Veilige Element-transaksiegeval"</string>
     <string name="permdesc_nfcTransactionEvent" msgid="1904286701876487397">"Laat die app toe om inligting te ontvang oor transaksies wat op ’n Veilige Element plaasvind."</string>
     <string name="permlab_disableKeyguard" msgid="3605253559020928505">"deaktiveer jou skermslot"</string>
@@ -628,17 +628,17 @@
     <string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"versoek skermslot-kompleksiteit"</string>
     <string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"Laat die app toe om die skermslot-kompleksiteitvlak (hoog, medium, laag of geen) te leer, wat die moontlike omvang van die lengte en soort skermslot aandui. Hierdie app kan ook aan gebruikers voorstel dat hulle die skermslot na \'n sekere vlak opdateer, maar gebruikers kan dit uit vrye wil ignoreer en weggaan. Let daarop dat die skermslot nie in skoonteks geberg word nie en die app dus nie die presiese wagwoord ken nie."</string>
     <string name="permlab_postNotification" msgid="4875401198597803658">"wys kennisgewings"</string>
-    <string name="permdesc_postNotification" msgid="5974977162462877075">"Laat die program toe om kennisgewings te wys"</string>
+    <string name="permdesc_postNotification" msgid="5974977162462877075">"Laat die app toe om kennisgewings te wys"</string>
     <string name="permlab_turnScreenOn" msgid="219344053664171492">"skakel die skerm aan"</string>
     <string name="permdesc_turnScreenOn" msgid="4394606875897601559">"Laat die app toe om die skerm aan te skakel."</string>
     <string name="permlab_useBiometric" msgid="6314741124749633786">"gebruik biometriese hardeware"</string>
-    <string name="permdesc_useBiometric" msgid="7502858732677143410">"Laat die program toe om biometriese hardeware vir stawing te gebruik"</string>
+    <string name="permdesc_useBiometric" msgid="7502858732677143410">"Laat die app toe om biometriese hardeware vir stawing te gebruik"</string>
     <string name="permlab_manageFingerprint" msgid="7432667156322821178">"bestuur vingerafdrukhardeware"</string>
     <string name="permdesc_manageFingerprint" msgid="2025616816437339865">"Laat die app toe om metodes te benut om vingerafdruktemplate vir gebruik by te voeg en uit te vee."</string>
     <string name="permlab_useFingerprint" msgid="1001421069766751922">"gebruik vingerafdrukhardeware"</string>
     <string name="permdesc_useFingerprint" msgid="412463055059323742">"Laat die app toe om vingerafdrukhardeware vir stawing te gebruik"</string>
     <string name="permlab_audioWrite" msgid="8501705294265669405">"wysig jou musiekversameling"</string>
-    <string name="permdesc_audioWrite" msgid="8057399517013412431">"Laat die program toe om jou musiekversameling te wysig."</string>
+    <string name="permdesc_audioWrite" msgid="8057399517013412431">"Laat die app toe om jou musiekversameling te wysig."</string>
     <string name="permlab_videoWrite" msgid="5940738769586451318">"wysig jou videoversameling"</string>
     <string name="permdesc_videoWrite" msgid="6124731210613317051">"Laat die app toe om jou videoversameling te wysig."</string>
     <string name="permlab_imagesWrite" msgid="1774555086984985578">"wysig jou fotoversameling"</string>
@@ -778,7 +778,7 @@
     <string name="permlab_readVisualUserSelect" msgid="5516204215354667586">"lees prent- en videolêers wat gebruiker in gedeelde berging kies"</string>
     <string name="permdesc_readVisualUserSelect" msgid="8027174717714968217">"Laat die app toe om prent- en videolêers te lees wat jy in jou gedeelde berging kies."</string>
     <string name="permlab_sdcardWrite" msgid="4863021819671416668">"verander of vee jou gedeelde berging se inhoud uit"</string>
-    <string name="permdesc_sdcardWrite" msgid="8376047679331387102">"Laat die program toe om jou gedeelde berging se inhoud te skryf."</string>
+    <string name="permdesc_sdcardWrite" msgid="8376047679331387102">"Laat die app toe om jou gedeelde berging se inhoud te skryf."</string>
     <string name="permlab_use_sip" msgid="8250774565189337477">"maak en/of ontvang SIP-oproepe"</string>
     <string name="permdesc_use_sip" msgid="3590270893253204451">"Laat die app toe om SIP-oproepe te maak en te ontvang."</string>
     <string name="permlab_register_sim_subscription" msgid="1653054249287576161">"registreer nuwe telekommunikasie-SIM-verbindings"</string>
@@ -800,7 +800,7 @@
     <string name="permlab_modifyNetworkAccounting" msgid="7448790834938749041">"verander verrekening van netwerkgebruik"</string>
     <string name="permdesc_modifyNetworkAccounting" msgid="5076042642247205390">"Laat die app toe om te verander hoe netwerkgebruik teenoor apps gemeet word. Nie vir gebruik deur normale apps nie."</string>
     <string name="permlab_accessNotifications" msgid="7130360248191984741">"kry toegang tot kennisgewings"</string>
-    <string name="permdesc_accessNotifications" msgid="761730149268789668">"Laat die program toe om kennisgewings op te haal, te bestudeer en te verwyder, insluitende die kennisgewings wat deur ander programme geplaas is."</string>
+    <string name="permdesc_accessNotifications" msgid="761730149268789668">"Laat die app toe om kennisgewings op te haal, te bestudeer en te verwyder, insluitende die kennisgewings wat deur ander apps geplaas is."</string>
     <string name="permlab_bindNotificationListenerService" msgid="5848096702733262458">"bind aan \'n kennisgewingluisteraardiens"</string>
     <string name="permdesc_bindNotificationListenerService" msgid="4970553694467137126">"Laat die houer toe om aan die top-koppelvlak van \'n kennisgewingluisteraardiens te bind. Behoort nooit vir gewone programme nodig te wees nie."</string>
     <string name="permlab_bindConditionProviderService" msgid="5245421224814878483">"verbind met \'n toestandverskafferdiens"</string>
@@ -824,15 +824,15 @@
     <string name="permlab_bindCarrierServices" msgid="2395596978626237474">"verbind aan diensverskafferdienste"</string>
     <string name="permdesc_bindCarrierServices" msgid="9185614481967262900">"Laat die houer toe om aan diensverskafferdienste te verbind. Behoort nooit vir normale programme nodig te wees nie."</string>
     <string name="permlab_access_notification_policy" msgid="5524112842876975537">"verkry toegang tot Moenie Steur Nie"</string>
-    <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Laat die program toe om Moenie Steur Nie-opstelling te lees en skryf."</string>
+    <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Laat die app toe om Moenie Steur Nie-opstelling te lees en skryf."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"begin kyk van toestemminggebruik"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Laat die houer toe om die toestemminggebruik vir \'n app te begin. Dit behoort nooit vir normale apps nodig te wees nie."</string>
     <string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"begin Bekyk Toestemmingbesluite"</string>
     <string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"Laat die houer toe om skerm te begin om toestemmingbesluite na te gaan. Behoort nooit vir normale programme nodig te wees nie."</string>
     <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"begin Bekyk Programkenmerke"</string>
-    <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Laat die houer toe om die kenmerke-inligting vir \'n program te begin bekyk."</string>
+    <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Laat die houer toe om die kenmerke-inligting vir \'n app te begin bekyk."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"kry toegang tot sensordata teen \'n hoë monsternemingkoers"</string>
-    <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Laat die program toe om monsters van sensordata teen \'n hoër koers as 200 Hz te neem"</string>
+    <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Laat die app toe om monsters van sensordata teen \'n hoër koers as 200 Hz te neem"</string>
     <string name="permlab_updatePackagesWithoutUserAction" msgid="3363272609642618551">"dateer app sonder gebruikerhandeling op"</string>
     <string name="permdesc_updatePackagesWithoutUserAction" msgid="4567739631260526366">"Laat die houer toe om die app wat dit voorheen sonder gebruikhandeling geïnstalleer het, op te dateer"</string>
     <string name="permlab_writeVerificationStateE2eeContactKeys" msgid="3990742344778360457">"dateer die verifikasiestatus van E2EE-kontaksleutels op wat deur ander apps besit word"</string>
@@ -1158,38 +1158,22 @@
     <string name="duration_hours_shortest_future" msgid="2979276794547981674">"oor <xliff:g id="COUNT">%d</xliff:g> u."</string>
     <string name="duration_days_shortest_future" msgid="3392722163935571543">"oor <xliff:g id="COUNT">%d</xliff:g> d."</string>
     <string name="duration_years_shortest_future" msgid="5537464088352970388">"oor <xliff:g id="COUNT">%d</xliff:g> j."</string>
-    <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
-    <skip />
-    <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
-    <skip />
-    <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
-    <skip />
-    <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium (1465359726485910115) -->
-    <skip />
-    <!-- no translation found for duration_days_medium (5994225628248661388) -->
-    <skip />
-    <!-- no translation found for duration_years_medium (734023884353592526) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_past (893797065424596243) -->
-    <skip />
+    <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g> m. gelede"</string>
+    <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g> uur gelede"</string>
+    <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g> d. gelede"</string>
+    <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g> jr. gelede"</string>
+    <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> min."</string>
+    <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> uur"</string>
+    <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> d."</string>
+    <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> jr."</string>
+    <string name="duration_minutes_medium_future" msgid="2750894988731934402">"oor <xliff:g id="COUNT">%d</xliff:g> min."</string>
+    <string name="duration_hours_medium_future" msgid="6050833881463849764">"oor <xliff:g id="COUNT">%d</xliff:g> uur"</string>
+    <string name="duration_days_medium_future" msgid="1700821545602729963">"oor <xliff:g id="COUNT">%d</xliff:g> d."</string>
+    <string name="duration_years_medium_future" msgid="3281018940397120166">"oor <xliff:g id="COUNT">%d</xliff:g> jr."</string>
+    <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g> min. gelede"</string>
+    <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g> uur gelede"</string>
+    <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g> d. gelede"</string>
+    <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g> jr. gelede"</string>
     <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# minuut gelede}other{# minute gelede}}"</string>
     <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# uur gelede}other{# uur gelede}}"</string>
     <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# dag gelede}other{# dae gelede}}"</string>
@@ -1272,7 +1256,7 @@
     <string name="whichSendToApplication" msgid="77101541959464018">"Stuur met"</string>
     <string name="whichSendToApplicationNamed" msgid="3385686512014670003">"Stuur met %1$s"</string>
     <string name="whichSendToApplicationLabel" msgid="3543240188816513303">"Stuur"</string>
-    <string name="whichHomeApplication" msgid="8276350727038396616">"Kies \'n Tuis-program"</string>
+    <string name="whichHomeApplication" msgid="8276350727038396616">"Kies \'n Tuis-app"</string>
     <string name="whichHomeApplicationNamed" msgid="5855990024847433794">"Gebruik %1$s as Tuis"</string>
     <string name="whichHomeApplicationLabel" msgid="8907334282202933959">"Vang prent vas"</string>
     <string name="whichImageCaptureApplication" msgid="2737413019463215284">"Vang prent vas met"</string>
@@ -1293,7 +1277,7 @@
     <string name="aerr_close" msgid="3398336821267021852">"Maak toe"</string>
     <string name="aerr_mute" msgid="2304972923480211376">"Demp totdat toestel herbegin"</string>
     <string name="aerr_wait" msgid="3198677780474548217">"Wag"</string>
-    <string name="aerr_close_app" msgid="8318883106083050970">"Maak program toe"</string>
+    <string name="aerr_close_app" msgid="8318883106083050970">"Maak app toe"</string>
     <string name="anr_title" msgid="7290329487067300120"></string>
     <string name="anr_activity_application" msgid="8121716632960340680">"<xliff:g id="APPLICATION">%2$s</xliff:g> reageer nie"</string>
     <string name="anr_activity_process" msgid="3477362583767128667">"<xliff:g id="ACTIVITY">%1$s</xliff:g> reageer nie"</string>
@@ -1311,7 +1295,7 @@
     <string name="screen_compat_mode_hint" msgid="4032272159093750908">"Heraktiveer hierdie in Stelselinstellings &gt; Programme &gt; Afgelaai."</string>
     <string name="unsupported_display_size_message" msgid="7265211375269394699">"<xliff:g id="APP_NAME">%1$s</xliff:g> steun nie die huidige skermgrootte-instelling nie en sal dalk onverwags reageer."</string>
     <string name="unsupported_display_size_show" msgid="980129850974919375">"Wys altyd"</string>
-    <string name="unsupported_compile_sdk_message" msgid="7326293500707890537">"<xliff:g id="APP_NAME">%1$s</xliff:g> is gebou vir \'n onversoenbare weergawe van die Android-bedryfstelsel en kan dalk op \'n onverwagte manier reageer. \'n Opgedateerde weergawe van die program is dalk beskikbaar."</string>
+    <string name="unsupported_compile_sdk_message" msgid="7326293500707890537">"<xliff:g id="APP_NAME">%1$s</xliff:g> is gebou vir \'n onversoenbare weergawe van die Android-bedryfstelsel en kan dalk op \'n onverwagte manier reageer. \'n Opgedateerde weergawe van die app is dalk beskikbaar."</string>
     <string name="unsupported_compile_sdk_show" msgid="1601210057960312248">"Wys altyd"</string>
     <string name="unsupported_compile_sdk_check_update" msgid="1103639989147664456">"Kyk vir opdatering"</string>
     <string name="smv_application" msgid="3775183542777792638">"Die app <xliff:g id="APPLICATION">%1$s</xliff:g> (proses <xliff:g id="PROCESS">%2$s</xliff:g>) het sy selfopgelegde StrictMode-beleid oortree."</string>
@@ -1541,7 +1525,7 @@
     <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"vra om batteryoptimerings te ignoreer"</string>
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Laat \'n app toe om toestemming te vra om batteryoptimerings vir daardie app te ignoreer."</string>
     <string name="permlab_queryAllPackages" msgid="2928450604653281650">"navraag oor alle pakkette"</string>
-    <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Laat \'n program toe om alle geïnstalleerde pakette te sien."</string>
+    <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Laat \'n app toe om alle geïnstalleerde pakette te sien."</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Klop twee keer vir zoembeheer"</string>
     <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Kon nie legstuk byvoeg nie."</string>
     <string name="ime_action_go" msgid="5536744546326495436">"Gaan"</string>
@@ -2040,10 +2024,10 @@
     <string name="language_picker_section_all" msgid="1985809075777564284">"Alle tale"</string>
     <string name="region_picker_section_all" msgid="756441309928774155">"Allle streke"</string>
     <string name="locale_search_menu" msgid="6258090710176422934">"Soek"</string>
-    <string name="app_suspended_title" msgid="888873445010322650">"Program is nie beskikbaar nie"</string>
+    <string name="app_suspended_title" msgid="888873445010322650">"App is nie beskikbaar nie"</string>
     <string name="app_suspended_default_message" msgid="6451215678552004172">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> is nie nou onmiddellik beskikbaar nie. Dit word bestuur deur <xliff:g id="APP_NAME_1">%2$s</xliff:g>."</string>
     <string name="app_suspended_more_details" msgid="211260942831587014">"Kom meer te wete"</string>
-    <string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Hervat program"</string>
+    <string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Hervat app"</string>
     <string name="work_mode_off_title" msgid="6367463960165135829">"Hervat werkapps?"</string>
     <string name="work_mode_turn_on" msgid="5316648862401307800">"Hervat"</string>
     <string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Noodgeval"</string>
@@ -2051,7 +2035,7 @@
     <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Stel skermslot"</string>
     <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Stel ’n skermslot op dié toestel om jou privaat ruimte te gebruik"</string>
     <string name="private_space_set_up_screen_lock_for_reset" msgid="7817091386408432097">"Stel ’n skermslot op dié toestel om privaat ruimte uit te vee"</string>
-    <string name="app_blocked_title" msgid="7353262160455028160">"Program is nie beskikbaar nie"</string>
+    <string name="app_blocked_title" msgid="7353262160455028160">"App is nie beskikbaar nie"</string>
     <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> is nie op die oomblik beskikbaar nie."</string>
     <string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> is nie beskikbaar nie"</string>
     <string name="app_streaming_blocked_title_for_permission_dialog" msgid="3805704317624448487">"Toestemmingsversoek is onderdruk"</string>
@@ -2163,7 +2147,7 @@
     <string name="shortcut_disabled_reason_unknown" msgid="753074793553599166">"Kortpad is gedeaktiveer"</string>
     <string name="harmful_app_warning_uninstall" msgid="6472912975664191772">"DEΪNSTALLEER"</string>
     <string name="harmful_app_warning_open_anyway" msgid="5963657791740211807">"MAAK TOG OOP"</string>
-    <string name="harmful_app_warning_title" msgid="8794823880881113856">"Skadelike program is bespeur"</string>
+    <string name="harmful_app_warning_title" msgid="8794823880881113856">"Skadelike app is bespeur"</string>
     <string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> wil <xliff:g id="APP_2">%2$s</xliff:g>-skyfies wys"</string>
     <string name="screenshot_edit" msgid="7408934887203689207">"Wysig"</string>
     <string name="volume_dialog_ringer_guidance_vibrate" msgid="2055927873175228519">"Oproepe en kennisgewings sal vibreer"</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index ad5bbcb..ecfce08 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -1158,38 +1158,22 @@
     <string name="duration_hours_shortest_future" msgid="2979276794547981674">"በ<xliff:g id="COUNT">%d</xliff:g> ሰ ውስጥ"</string>
     <string name="duration_days_shortest_future" msgid="3392722163935571543">"በ<xliff:g id="COUNT">%d</xliff:g> ቀ ውስጥ"</string>
     <string name="duration_years_shortest_future" msgid="5537464088352970388">"በ<xliff:g id="COUNT">%d</xliff:g> ዓ ውስጥ"</string>
-    <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
-    <skip />
-    <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
-    <skip />
-    <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
-    <skip />
-    <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium (1465359726485910115) -->
-    <skip />
-    <!-- no translation found for duration_days_medium (5994225628248661388) -->
-    <skip />
-    <!-- no translation found for duration_years_medium (734023884353592526) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_past (893797065424596243) -->
-    <skip />
+    <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"ከ<xliff:g id="COUNT">%d</xliff:g>ደ በፊት"</string>
+    <string name="duration_hours_shortest_past" msgid="2098397414186628489">"ከ<xliff:g id="COUNT">%d</xliff:g>ሰዓ በፊት"</string>
+    <string name="duration_days_shortest_past" msgid="1832006037955897625">"ከ<xliff:g id="COUNT">%d</xliff:g>ቀ በፊት"</string>
+    <string name="duration_years_shortest_past" msgid="6168256514200469291">"ከ<xliff:g id="COUNT">%d</xliff:g>ዓ በፊት"</string>
+    <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g>ደቂቃ"</string>
+    <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g>ሰዓ"</string>
+    <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g>ቀ"</string>
+    <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g>ዓመ"</string>
+    <string name="duration_minutes_medium_future" msgid="2750894988731934402">"በ<xliff:g id="COUNT">%d</xliff:g>ደቂቃ ውስጥ"</string>
+    <string name="duration_hours_medium_future" msgid="6050833881463849764">"በ<xliff:g id="COUNT">%d</xliff:g>ሰዓ ውስጥ"</string>
+    <string name="duration_days_medium_future" msgid="1700821545602729963">"በ<xliff:g id="COUNT">%d</xliff:g>ቀ ውስጥ"</string>
+    <string name="duration_years_medium_future" msgid="3281018940397120166">"በ<xliff:g id="COUNT">%d</xliff:g>ዓመ ውስጥ"</string>
+    <string name="duration_minutes_medium_past" msgid="7400424340181947714">"ከ<xliff:g id="COUNT">%d</xliff:g>ደቂቃ በፊት"</string>
+    <string name="duration_hours_medium_past" msgid="6709441336035202617">"ከ<xliff:g id="COUNT">%d</xliff:g>ሰዓ በፊት"</string>
+    <string name="duration_days_medium_past" msgid="5748156261134344532">"ከ<xliff:g id="COUNT">%d</xliff:g>ቀ በፊት"</string>
+    <string name="duration_years_medium_past" msgid="893797065424596243">"ከ<xliff:g id="COUNT">%d</xliff:g>ዓመ በፊት"</string>
     <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{ከ# ደቂቃ በፊት}one{# ደቂቃ በፊት}other{# ደቂቃዎች በፊት}}"</string>
     <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{ከ# ሰዓት በፊት}one{ከ# ሰዓት በፊት}other{ከ# ሰዓታት በፊት}}"</string>
     <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{ከ# ቀን በፊት}one{ከ# ቀን በፊት}other{ከ# ቀናት በፊት}}"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 8cb64c5..dc047ac 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -1162,38 +1162,22 @@
     <string name="duration_hours_shortest_future" msgid="2979276794547981674">"خلال <xliff:g id="COUNT">%d</xliff:g> ساعة"</string>
     <string name="duration_days_shortest_future" msgid="3392722163935571543">"خلال <xliff:g id="COUNT">%d</xliff:g> يوم"</string>
     <string name="duration_years_shortest_future" msgid="5537464088352970388">"خلال <xliff:g id="COUNT">%d</xliff:g> سنة"</string>
-    <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
-    <skip />
-    <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
-    <skip />
-    <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
-    <skip />
-    <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium (1465359726485910115) -->
-    <skip />
-    <!-- no translation found for duration_days_medium (5994225628248661388) -->
-    <skip />
-    <!-- no translation found for duration_years_medium (734023884353592526) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_past (893797065424596243) -->
-    <skip />
+    <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"قبل <xliff:g id="COUNT">%d</xliff:g> دقيقة"</string>
+    <string name="duration_hours_shortest_past" msgid="2098397414186628489">"قبل <xliff:g id="COUNT">%d</xliff:g> ساعة"</string>
+    <string name="duration_days_shortest_past" msgid="1832006037955897625">"قبل <xliff:g id="COUNT">%d</xliff:g> يوم"</string>
+    <string name="duration_years_shortest_past" msgid="6168256514200469291">"قبل <xliff:g id="COUNT">%d</xliff:g> سنة"</string>
+    <string name="duration_minutes_medium" msgid="5891933490342643944">"‫<xliff:g id="COUNT">%d</xliff:g> دقيقة"</string>
+    <string name="duration_hours_medium" msgid="1465359726485910115">"‫<xliff:g id="COUNT">%d</xliff:g> ساعة"</string>
+    <string name="duration_days_medium" msgid="5994225628248661388">"‫<xliff:g id="COUNT">%d</xliff:g> يوم"</string>
+    <string name="duration_years_medium" msgid="734023884353592526">"‫<xliff:g id="COUNT">%d</xliff:g> سنة"</string>
+    <string name="duration_minutes_medium_future" msgid="2750894988731934402">"خلال <xliff:g id="COUNT">%d</xliff:g> دقيقة"</string>
+    <string name="duration_hours_medium_future" msgid="6050833881463849764">"خلال <xliff:g id="COUNT">%d</xliff:g> ساعة"</string>
+    <string name="duration_days_medium_future" msgid="1700821545602729963">"خلال <xliff:g id="COUNT">%d</xliff:g> يوم"</string>
+    <string name="duration_years_medium_future" msgid="3281018940397120166">"خلال <xliff:g id="COUNT">%d</xliff:g> سنة"</string>
+    <string name="duration_minutes_medium_past" msgid="7400424340181947714">"قبل <xliff:g id="COUNT">%d</xliff:g> دقيقة"</string>
+    <string name="duration_hours_medium_past" msgid="6709441336035202617">"قبل <xliff:g id="COUNT">%d</xliff:g> ساعة"</string>
+    <string name="duration_days_medium_past" msgid="5748156261134344532">"قبل <xliff:g id="COUNT">%d</xliff:g> يوم"</string>
+    <string name="duration_years_medium_past" msgid="893797065424596243">"قبل <xliff:g id="COUNT">%d</xliff:g> سنة"</string>
     <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{قبل دقيقة واحدة}zero{قبل # دقيقة}two{قبل دقيقتين}few{قبل # دقائق}many{قبل # دقيقة}other{قبل # دقيقة}}"</string>
     <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{قبل ساعة واحدة}zero{قبل # ساعة}two{قبل ساعتين}few{قبل # ساعات}many{قبل # ساعة}other{قبل # ساعة}}"</string>
     <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{قبل يوم واحد}zero{قبل # يوم}two{قبل يومين}few{قبل # أيام}many{قبل # يومًا}other{قبل # يوم}}"</string>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index 4214ebe..402394d0 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -1158,38 +1158,22 @@
     <string name="duration_hours_shortest_future" msgid="2979276794547981674">"<xliff:g id="COUNT">%d</xliff:g> ঘণ্টাত"</string>
     <string name="duration_days_shortest_future" msgid="3392722163935571543">"<xliff:g id="COUNT">%d</xliff:g>দিনত"</string>
     <string name="duration_years_shortest_future" msgid="5537464088352970388">"<xliff:g id="COUNT">%d</xliff:g> বছৰত"</string>
-    <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
-    <skip />
-    <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
-    <skip />
-    <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
-    <skip />
-    <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium (1465359726485910115) -->
-    <skip />
-    <!-- no translation found for duration_days_medium (5994225628248661388) -->
-    <skip />
-    <!-- no translation found for duration_years_medium (734023884353592526) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_past (893797065424596243) -->
-    <skip />
+    <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g> মিনিট পূৰ্বে"</string>
+    <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g> ঘণ্টা পূৰ্বে"</string>
+    <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g> দিন পূৰ্বে"</string>
+    <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g> বছৰৰ পূৰ্বে"</string>
+    <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> মিনিট"</string>
+    <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> ঘণ্টা"</string>
+    <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> দিন"</string>
+    <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> বছৰ"</string>
+    <string name="duration_minutes_medium_future" msgid="2750894988731934402">"<xliff:g id="COUNT">%d</xliff:g> মিনিটত"</string>
+    <string name="duration_hours_medium_future" msgid="6050833881463849764">"<xliff:g id="COUNT">%d</xliff:g> ঘণ্টাত"</string>
+    <string name="duration_days_medium_future" msgid="1700821545602729963">"<xliff:g id="COUNT">%d</xliff:g> দিনত"</string>
+    <string name="duration_years_medium_future" msgid="3281018940397120166">"<xliff:g id="COUNT">%d</xliff:g> বছৰত"</string>
+    <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g> মিনিট পূৰ্বে"</string>
+    <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g> ঘণ্টা পূৰ্বে"</string>
+    <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g> দিন পূৰ্বে"</string>
+    <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g> বছৰৰ পূৰ্বে"</string>
     <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# মিনিট পূৰ্বে}one{# মিনিট পূৰ্বে}other{# মিনিট পূৰ্বে}}"</string>
     <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# ঘণ্টা পূৰ্বে}one{# ঘণ্টা পূৰ্বে}other{# ঘণ্টা পূৰ্বে}}"</string>
     <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# দিন পূর্বে}one{# দিন পূৰ্বে}other{# দিন পূৰ্বে}}"</string>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index d4389dc..c696e63 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -1158,38 +1158,22 @@
     <string name="duration_hours_shortest_future" msgid="2979276794547981674">"<xliff:g id="COUNT">%d</xliff:g> s ərzində"</string>
     <string name="duration_days_shortest_future" msgid="3392722163935571543">"<xliff:g id="COUNT">%d</xliff:g> g ərzində"</string>
     <string name="duration_years_shortest_future" msgid="5537464088352970388">"<xliff:g id="COUNT">%d</xliff:g> il ərzində"</string>
-    <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
-    <skip />
-    <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
-    <skip />
-    <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
-    <skip />
-    <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium (1465359726485910115) -->
-    <skip />
-    <!-- no translation found for duration_days_medium (5994225628248661388) -->
-    <skip />
-    <!-- no translation found for duration_years_medium (734023884353592526) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_past (893797065424596243) -->
-    <skip />
+    <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g> dəqiqə əvvəl"</string>
+    <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g> saat əvvəl"</string>
+    <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g> gün əvvəl"</string>
+    <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g> il əvvəl"</string>
+    <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> dəqiqə"</string>
+    <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> saat"</string>
+    <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> gün"</string>
+    <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> il"</string>
+    <string name="duration_minutes_medium_future" msgid="2750894988731934402">"<xliff:g id="COUNT">%d</xliff:g> dəqiqə ərzində"</string>
+    <string name="duration_hours_medium_future" msgid="6050833881463849764">"<xliff:g id="COUNT">%d</xliff:g> saat ərzində"</string>
+    <string name="duration_days_medium_future" msgid="1700821545602729963">"<xliff:g id="COUNT">%d</xliff:g> gün ərzində"</string>
+    <string name="duration_years_medium_future" msgid="3281018940397120166">"<xliff:g id="COUNT">%d</xliff:g> il ərzində"</string>
+    <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g> dəqiqə əvvəl"</string>
+    <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g> saat əvvəl"</string>
+    <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g> gün əvvəl"</string>
+    <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g> il əvvəl"</string>
     <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# dəqiqə əvvəl}other{# dəqiqə əvvəl}}"</string>
     <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# saat əvvəl}other{# saat əvvəl}}"</string>
     <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# gün əvvəl}other{# gün əvvəl}}"</string>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index 911eede..dc39346 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -1159,38 +1159,22 @@
     <string name="duration_hours_shortest_future" msgid="2979276794547981674">"za <xliff:g id="COUNT">%d</xliff:g> s"</string>
     <string name="duration_days_shortest_future" msgid="3392722163935571543">"za <xliff:g id="COUNT">%d</xliff:g> d"</string>
     <string name="duration_years_shortest_future" msgid="5537464088352970388">"za <xliff:g id="COUNT">%d</xliff:g> god"</string>
-    <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
-    <skip />
-    <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
-    <skip />
-    <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
-    <skip />
-    <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium (1465359726485910115) -->
-    <skip />
-    <!-- no translation found for duration_days_medium (5994225628248661388) -->
-    <skip />
-    <!-- no translation found for duration_years_medium (734023884353592526) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_past (893797065424596243) -->
-    <skip />
+    <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"pre <xliff:g id="COUNT">%d</xliff:g> min"</string>
+    <string name="duration_hours_shortest_past" msgid="2098397414186628489">"pre <xliff:g id="COUNT">%d</xliff:g> s"</string>
+    <string name="duration_days_shortest_past" msgid="1832006037955897625">"pre <xliff:g id="COUNT">%d</xliff:g> d"</string>
+    <string name="duration_years_shortest_past" msgid="6168256514200469291">"pre <xliff:g id="COUNT">%d</xliff:g> g"</string>
+    <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> min"</string>
+    <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> s"</string>
+    <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> d"</string>
+    <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> god"</string>
+    <string name="duration_minutes_medium_future" msgid="2750894988731934402">"za <xliff:g id="COUNT">%d</xliff:g> min"</string>
+    <string name="duration_hours_medium_future" msgid="6050833881463849764">"za <xliff:g id="COUNT">%d</xliff:g> s"</string>
+    <string name="duration_days_medium_future" msgid="1700821545602729963">"za <xliff:g id="COUNT">%d</xliff:g> d"</string>
+    <string name="duration_years_medium_future" msgid="3281018940397120166">"za <xliff:g id="COUNT">%d</xliff:g> god"</string>
+    <string name="duration_minutes_medium_past" msgid="7400424340181947714">"pre <xliff:g id="COUNT">%d</xliff:g> min"</string>
+    <string name="duration_hours_medium_past" msgid="6709441336035202617">"pre <xliff:g id="COUNT">%d</xliff:g> s"</string>
+    <string name="duration_days_medium_past" msgid="5748156261134344532">"pre <xliff:g id="COUNT">%d</xliff:g> d"</string>
+    <string name="duration_years_medium_past" msgid="893797065424596243">"pre <xliff:g id="COUNT">%d</xliff:g> god"</string>
     <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{Pre # minut}one{Pre # minut}few{Pre # minuta}other{Pre # minuta}}"</string>
     <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{Pre # sat}one{Pre # sat}few{Pre # sata}other{Pre # sati}}"</string>
     <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{Pre # dan}one{Pre # dan}few{Pre # dana}other{Pre # dana}}"</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index 0165929..d1a2104 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -1160,38 +1160,22 @@
     <string name="duration_hours_shortest_future" msgid="2979276794547981674">"праз <xliff:g id="COUNT">%d</xliff:g> гадз"</string>
     <string name="duration_days_shortest_future" msgid="3392722163935571543">"праз <xliff:g id="COUNT">%d</xliff:g> сут"</string>
     <string name="duration_years_shortest_future" msgid="5537464088352970388">"праз <xliff:g id="COUNT">%d</xliff:g> г."</string>
-    <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
-    <skip />
-    <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
-    <skip />
-    <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
-    <skip />
-    <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium (1465359726485910115) -->
-    <skip />
-    <!-- no translation found for duration_days_medium (5994225628248661388) -->
-    <skip />
-    <!-- no translation found for duration_years_medium (734023884353592526) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_past (893797065424596243) -->
-    <skip />
+    <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g> хв таму"</string>
+    <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g> гадз таму"</string>
+    <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g> сут таму"</string>
+    <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g> г. таму"</string>
+    <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> хв"</string>
+    <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> гадз"</string>
+    <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> сут"</string>
+    <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> г."</string>
+    <string name="duration_minutes_medium_future" msgid="2750894988731934402">"праз <xliff:g id="COUNT">%d</xliff:g> хв"</string>
+    <string name="duration_hours_medium_future" msgid="6050833881463849764">"праз <xliff:g id="COUNT">%d</xliff:g> гадз"</string>
+    <string name="duration_days_medium_future" msgid="1700821545602729963">"праз <xliff:g id="COUNT">%d</xliff:g> сут"</string>
+    <string name="duration_years_medium_future" msgid="3281018940397120166">"праз <xliff:g id="COUNT">%d</xliff:g> г."</string>
+    <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g> хв таму"</string>
+    <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g> гадз таму"</string>
+    <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g> сут таму"</string>
+    <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g> г. таму"</string>
     <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# хвіліну таму}one{# хвіліну таму}few{# хвіліны таму}many{# хвілін таму}other{# хвіліны таму}}"</string>
     <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# гадзіну таму}one{# гадзіну таму}few{# гадзіны таму}many{# гадзін таму}other{# гадзіны таму}}"</string>
     <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# дзень таму}one{# дзень таму}few{# дні таму}many{# дзён таму}other{# дня таму}}"</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 1b68675..3332195 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -1158,38 +1158,22 @@
     <string name="duration_hours_shortest_future" msgid="2979276794547981674">"след <xliff:g id="COUNT">%d</xliff:g> ч"</string>
     <string name="duration_days_shortest_future" msgid="3392722163935571543">"след <xliff:g id="COUNT">%d</xliff:g> д"</string>
     <string name="duration_years_shortest_future" msgid="5537464088352970388">"след <xliff:g id="COUNT">%d</xliff:g> г."</string>
-    <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
-    <skip />
-    <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
-    <skip />
-    <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
-    <skip />
-    <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium (1465359726485910115) -->
-    <skip />
-    <!-- no translation found for duration_days_medium (5994225628248661388) -->
-    <skip />
-    <!-- no translation found for duration_years_medium (734023884353592526) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_past (893797065424596243) -->
-    <skip />
+    <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"преди <xliff:g id="COUNT">%d</xliff:g> мин"</string>
+    <string name="duration_hours_shortest_past" msgid="2098397414186628489">"преди <xliff:g id="COUNT">%d</xliff:g> ч"</string>
+    <string name="duration_days_shortest_past" msgid="1832006037955897625">"преди <xliff:g id="COUNT">%d</xliff:g> д"</string>
+    <string name="duration_years_shortest_past" msgid="6168256514200469291">"преди <xliff:g id="COUNT">%d</xliff:g> г."</string>
+    <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> мин"</string>
+    <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> ч"</string>
+    <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> д"</string>
+    <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> г."</string>
+    <string name="duration_minutes_medium_future" msgid="2750894988731934402">"след <xliff:g id="COUNT">%d</xliff:g> мин"</string>
+    <string name="duration_hours_medium_future" msgid="6050833881463849764">"след <xliff:g id="COUNT">%d</xliff:g> ч"</string>
+    <string name="duration_days_medium_future" msgid="1700821545602729963">"след <xliff:g id="COUNT">%d</xliff:g> д"</string>
+    <string name="duration_years_medium_future" msgid="3281018940397120166">"след <xliff:g id="COUNT">%d</xliff:g> г."</string>
+    <string name="duration_minutes_medium_past" msgid="7400424340181947714">"преди <xliff:g id="COUNT">%d</xliff:g> мин"</string>
+    <string name="duration_hours_medium_past" msgid="6709441336035202617">"преди <xliff:g id="COUNT">%d</xliff:g> ч"</string>
+    <string name="duration_days_medium_past" msgid="5748156261134344532">"преди <xliff:g id="COUNT">%d</xliff:g> д"</string>
+    <string name="duration_years_medium_past" msgid="893797065424596243">"преди <xliff:g id="COUNT">%d</xliff:g> г."</string>
     <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{преди # минута}other{преди # минути}}"</string>
     <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{преди # час}other{преди # часа}}"</string>
     <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{преди # ден}other{преди # дни}}"</string>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index 3b832e0..2b19c57 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -1158,38 +1158,22 @@
     <string name="duration_hours_shortest_future" msgid="2979276794547981674">"<xliff:g id="COUNT">%d</xliff:g>ঘণ্টার মধ্যে"</string>
     <string name="duration_days_shortest_future" msgid="3392722163935571543">"<xliff:g id="COUNT">%d</xliff:g>দিনের মধ্যে"</string>
     <string name="duration_years_shortest_future" msgid="5537464088352970388">"<xliff:g id="COUNT">%d</xliff:g>বছরের মধ্যে"</string>
-    <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
-    <skip />
-    <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
-    <skip />
-    <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
-    <skip />
-    <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium (1465359726485910115) -->
-    <skip />
-    <!-- no translation found for duration_days_medium (5994225628248661388) -->
-    <skip />
-    <!-- no translation found for duration_years_medium (734023884353592526) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_past (893797065424596243) -->
-    <skip />
+    <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g>মিনিট আগে"</string>
+    <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g>ঘণ্টা আগে"</string>
+    <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g>দিন আগে"</string>
+    <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g>বছর আগে"</string>
+    <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g>মিনিট"</string>
+    <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g>ঘণ্টা"</string>
+    <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g>দিন"</string>
+    <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g>বছর"</string>
+    <string name="duration_minutes_medium_future" msgid="2750894988731934402">"<xliff:g id="COUNT">%d</xliff:g>মিনিটের মধ্যে"</string>
+    <string name="duration_hours_medium_future" msgid="6050833881463849764">"<xliff:g id="COUNT">%d</xliff:g>ঘণ্টার মধ্যে"</string>
+    <string name="duration_days_medium_future" msgid="1700821545602729963">"<xliff:g id="COUNT">%d</xliff:g>দিনের মধ্যে"</string>
+    <string name="duration_years_medium_future" msgid="3281018940397120166">"<xliff:g id="COUNT">%d</xliff:g>বছরের মধ্যে"</string>
+    <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g>মিনিট আগে"</string>
+    <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g>ঘন্টা আগে"</string>
+    <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g>দিন আগে"</string>
+    <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g>বছর আগে"</string>
     <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# মিনিট আগে}one{# মিনিট আগে}other{# মিনিট আগে}}"</string>
     <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# ঘণ্টা আগে}one{# ঘণ্টা আগে}other{# ঘণ্টা আগে}}"</string>
     <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# দিন আগে}one{# দিন আগে}other{# দিন আগে}}"</string>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index 64ded2d..c49e337 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -1159,38 +1159,22 @@
     <string name="duration_hours_shortest_future" msgid="2979276794547981674">"za <xliff:g id="COUNT">%d</xliff:g> h"</string>
     <string name="duration_days_shortest_future" msgid="3392722163935571543">"za <xliff:g id="COUNT">%d</xliff:g> d"</string>
     <string name="duration_years_shortest_future" msgid="5537464088352970388">"za <xliff:g id="COUNT">%d</xliff:g> g"</string>
-    <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
-    <skip />
-    <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
-    <skip />
-    <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
-    <skip />
-    <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium (1465359726485910115) -->
-    <skip />
-    <!-- no translation found for duration_days_medium (5994225628248661388) -->
-    <skip />
-    <!-- no translation found for duration_years_medium (734023884353592526) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_past (893797065424596243) -->
-    <skip />
+    <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"prije <xliff:g id="COUNT">%d</xliff:g> min"</string>
+    <string name="duration_hours_shortest_past" msgid="2098397414186628489">"prije <xliff:g id="COUNT">%d</xliff:g> h"</string>
+    <string name="duration_days_shortest_past" msgid="1832006037955897625">"prije <xliff:g id="COUNT">%d</xliff:g> dan(a)"</string>
+    <string name="duration_years_shortest_past" msgid="6168256514200469291">"prije <xliff:g id="COUNT">%d</xliff:g> god."</string>
+    <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> min"</string>
+    <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> h"</string>
+    <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> dan(a)"</string>
+    <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> god."</string>
+    <string name="duration_minutes_medium_future" msgid="2750894988731934402">"za <xliff:g id="COUNT">%d</xliff:g> min"</string>
+    <string name="duration_hours_medium_future" msgid="6050833881463849764">"za <xliff:g id="COUNT">%d</xliff:g> h"</string>
+    <string name="duration_days_medium_future" msgid="1700821545602729963">"za <xliff:g id="COUNT">%d</xliff:g> dan(a)"</string>
+    <string name="duration_years_medium_future" msgid="3281018940397120166">"za <xliff:g id="COUNT">%d</xliff:g> god."</string>
+    <string name="duration_minutes_medium_past" msgid="7400424340181947714">"prije <xliff:g id="COUNT">%d</xliff:g> min"</string>
+    <string name="duration_hours_medium_past" msgid="6709441336035202617">"prije <xliff:g id="COUNT">%d</xliff:g> h"</string>
+    <string name="duration_days_medium_past" msgid="5748156261134344532">"prije <xliff:g id="COUNT">%d</xliff:g> dan(a)"</string>
+    <string name="duration_years_medium_past" msgid="893797065424596243">"prije <xliff:g id="COUNT">%d</xliff:g> god."</string>
     <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{Prije # min}one{Prije # min}few{Prije # min}other{Prije # min}}"</string>
     <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{Prije # h}one{Prije # h}few{Prije # h}other{Prije # h}}"</string>
     <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{Prije # dan}one{Prije # dan}few{Prije # dana}other{Prije # dana}}"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index d11941af3..3bb58a5 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -1159,38 +1159,22 @@
     <string name="duration_hours_shortest_future" msgid="2979276794547981674">"d\'aquí a <xliff:g id="COUNT">%d</xliff:g> h"</string>
     <string name="duration_days_shortest_future" msgid="3392722163935571543">"d\'aquí a <xliff:g id="COUNT">%d</xliff:g> d"</string>
     <string name="duration_years_shortest_future" msgid="5537464088352970388">"d\'aquí a <xliff:g id="COUNT">%d</xliff:g> a"</string>
-    <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
-    <skip />
-    <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
-    <skip />
-    <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
-    <skip />
-    <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium (1465359726485910115) -->
-    <skip />
-    <!-- no translation found for duration_days_medium (5994225628248661388) -->
-    <skip />
-    <!-- no translation found for duration_years_medium (734023884353592526) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_past (893797065424596243) -->
-    <skip />
+    <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"fa <xliff:g id="COUNT">%d</xliff:g> m"</string>
+    <string name="duration_hours_shortest_past" msgid="2098397414186628489">"fa <xliff:g id="COUNT">%d</xliff:g> h"</string>
+    <string name="duration_days_shortest_past" msgid="1832006037955897625">"fa <xliff:g id="COUNT">%d</xliff:g> d"</string>
+    <string name="duration_years_shortest_past" msgid="6168256514200469291">"fa <xliff:g id="COUNT">%d</xliff:g> a"</string>
+    <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> min"</string>
+    <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> hora"</string>
+    <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> dia"</string>
+    <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> any"</string>
+    <string name="duration_minutes_medium_future" msgid="2750894988731934402">"d\'aquí a <xliff:g id="COUNT">%d</xliff:g> min"</string>
+    <string name="duration_hours_medium_future" msgid="6050833881463849764">"d\'aquí a <xliff:g id="COUNT">%d</xliff:g> hora"</string>
+    <string name="duration_days_medium_future" msgid="1700821545602729963">"d\'aquí a <xliff:g id="COUNT">%d</xliff:g> dia"</string>
+    <string name="duration_years_medium_future" msgid="3281018940397120166">"d\'aquí a <xliff:g id="COUNT">%d</xliff:g> any"</string>
+    <string name="duration_minutes_medium_past" msgid="7400424340181947714">"fa <xliff:g id="COUNT">%d</xliff:g> min"</string>
+    <string name="duration_hours_medium_past" msgid="6709441336035202617">"fa <xliff:g id="COUNT">%d</xliff:g> hora"</string>
+    <string name="duration_days_medium_past" msgid="5748156261134344532">"fa <xliff:g id="COUNT">%d</xliff:g> dia"</string>
+    <string name="duration_years_medium_past" msgid="893797065424596243">"fa <xliff:g id="COUNT">%d</xliff:g> any"</string>
     <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{Fa # minut}many{Fa # minuts}other{Fa # minuts}}"</string>
     <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{Fa # hora}many{Fa # hores}other{Fa # hores}}"</string>
     <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{Fa # dia}many{Fa # dies}other{Fa # dies}}"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 50c3e6a..8038a29 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -1160,38 +1160,22 @@
     <string name="duration_hours_shortest_future" msgid="2979276794547981674">"za <xliff:g id="COUNT">%d</xliff:g> h"</string>
     <string name="duration_days_shortest_future" msgid="3392722163935571543">"za <xliff:g id="COUNT">%d</xliff:g> d"</string>
     <string name="duration_years_shortest_future" msgid="5537464088352970388">"za <xliff:g id="COUNT">%d</xliff:g> r"</string>
-    <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
-    <skip />
-    <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
-    <skip />
-    <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
-    <skip />
-    <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium (1465359726485910115) -->
-    <skip />
-    <!-- no translation found for duration_days_medium (5994225628248661388) -->
-    <skip />
-    <!-- no translation found for duration_years_medium (734023884353592526) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_past (893797065424596243) -->
-    <skip />
+    <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"před <xliff:g id="COUNT">%d</xliff:g> min"</string>
+    <string name="duration_hours_shortest_past" msgid="2098397414186628489">"před <xliff:g id="COUNT">%d</xliff:g> h"</string>
+    <string name="duration_days_shortest_past" msgid="1832006037955897625">"před <xliff:g id="COUNT">%d</xliff:g> d"</string>
+    <string name="duration_years_shortest_past" msgid="6168256514200469291">"před <xliff:g id="COUNT">%d</xliff:g> r"</string>
+    <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> min"</string>
+    <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> h"</string>
+    <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> d"</string>
+    <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> r"</string>
+    <string name="duration_minutes_medium_future" msgid="2750894988731934402">"za <xliff:g id="COUNT">%d</xliff:g> min"</string>
+    <string name="duration_hours_medium_future" msgid="6050833881463849764">"za <xliff:g id="COUNT">%d</xliff:g> h"</string>
+    <string name="duration_days_medium_future" msgid="1700821545602729963">"za <xliff:g id="COUNT">%d</xliff:g> d"</string>
+    <string name="duration_years_medium_future" msgid="3281018940397120166">"za <xliff:g id="COUNT">%d</xliff:g> r"</string>
+    <string name="duration_minutes_medium_past" msgid="7400424340181947714">"před <xliff:g id="COUNT">%d</xliff:g> min"</string>
+    <string name="duration_hours_medium_past" msgid="6709441336035202617">"před <xliff:g id="COUNT">%d</xliff:g> h"</string>
+    <string name="duration_days_medium_past" msgid="5748156261134344532">"před <xliff:g id="COUNT">%d</xliff:g> d"</string>
+    <string name="duration_years_medium_past" msgid="893797065424596243">"před <xliff:g id="COUNT">%d</xliff:g> r"</string>
     <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{před # minutou}few{před # minutami}many{před # minuty}other{před # minutami}}"</string>
     <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{před # hodinou}few{před # hodinami}many{před # hodiny}other{před # hodinami}}"</string>
     <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{Před # dnem}few{před # dny}many{před # dne}other{před # dny}}"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 9bd5f70..5403cce 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -1158,38 +1158,22 @@
     <string name="duration_hours_shortest_future" msgid="2979276794547981674">"om <xliff:g id="COUNT">%d</xliff:g> t."</string>
     <string name="duration_days_shortest_future" msgid="3392722163935571543">"om <xliff:g id="COUNT">%d</xliff:g> d."</string>
     <string name="duration_years_shortest_future" msgid="5537464088352970388">"om <xliff:g id="COUNT">%d</xliff:g> år"</string>
-    <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
-    <skip />
-    <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
-    <skip />
-    <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
-    <skip />
-    <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium (1465359726485910115) -->
-    <skip />
-    <!-- no translation found for duration_days_medium (5994225628248661388) -->
-    <skip />
-    <!-- no translation found for duration_years_medium (734023884353592526) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_past (893797065424596243) -->
-    <skip />
+    <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g> min. siden"</string>
+    <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g> t. siden"</string>
+    <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g> d. siden"</string>
+    <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g> år siden"</string>
+    <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> min."</string>
+    <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> t."</string>
+    <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> d."</string>
+    <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> år"</string>
+    <string name="duration_minutes_medium_future" msgid="2750894988731934402">"om <xliff:g id="COUNT">%d</xliff:g> min."</string>
+    <string name="duration_hours_medium_future" msgid="6050833881463849764">"om <xliff:g id="COUNT">%d</xliff:g> t."</string>
+    <string name="duration_days_medium_future" msgid="1700821545602729963">"om <xliff:g id="COUNT">%d</xliff:g> d."</string>
+    <string name="duration_years_medium_future" msgid="3281018940397120166">"om <xliff:g id="COUNT">%d</xliff:g> år"</string>
+    <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g> min. siden"</string>
+    <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g> t. siden"</string>
+    <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g> d. siden"</string>
+    <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g> år siden"</string>
     <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{For # minut siden}one{For # minut siden}other{For # minutter siden}}"</string>
     <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# time siden}one{# time siden}other{# timer siden}}"</string>
     <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{1 dag siden}one{1 dag siden}other{# dag siden}}"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 06cadd5..ca542ba 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -1158,38 +1158,22 @@
     <string name="duration_hours_shortest_future" msgid="2979276794547981674">"in <xliff:g id="COUNT">%d</xliff:g> Std."</string>
     <string name="duration_days_shortest_future" msgid="3392722163935571543">"in <xliff:g id="COUNT">%d</xliff:g> T"</string>
     <string name="duration_years_shortest_future" msgid="5537464088352970388">"in <xliff:g id="COUNT">%d</xliff:g> J"</string>
-    <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
-    <skip />
-    <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
-    <skip />
-    <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
-    <skip />
-    <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium (1465359726485910115) -->
-    <skip />
-    <!-- no translation found for duration_days_medium (5994225628248661388) -->
-    <skip />
-    <!-- no translation found for duration_years_medium (734023884353592526) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_past (893797065424596243) -->
-    <skip />
+    <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"vor <xliff:g id="COUNT">%d</xliff:g> Min."</string>
+    <string name="duration_hours_shortest_past" msgid="2098397414186628489">"vor <xliff:g id="COUNT">%d</xliff:g> Std."</string>
+    <string name="duration_days_shortest_past" msgid="1832006037955897625">"vor <xliff:g id="COUNT">%d</xliff:g> Tg."</string>
+    <string name="duration_years_shortest_past" msgid="6168256514200469291">"vor <xliff:g id="COUNT">%d</xliff:g> J."</string>
+    <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> Min."</string>
+    <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> Std."</string>
+    <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> Tg."</string>
+    <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> J."</string>
+    <string name="duration_minutes_medium_future" msgid="2750894988731934402">"in <xliff:g id="COUNT">%d</xliff:g> Min."</string>
+    <string name="duration_hours_medium_future" msgid="6050833881463849764">"in <xliff:g id="COUNT">%d</xliff:g> Std."</string>
+    <string name="duration_days_medium_future" msgid="1700821545602729963">"in <xliff:g id="COUNT">%d</xliff:g> Tg."</string>
+    <string name="duration_years_medium_future" msgid="3281018940397120166">"in <xliff:g id="COUNT">%d</xliff:g> J."</string>
+    <string name="duration_minutes_medium_past" msgid="7400424340181947714">"vor <xliff:g id="COUNT">%d</xliff:g> Min."</string>
+    <string name="duration_hours_medium_past" msgid="6709441336035202617">"vor <xliff:g id="COUNT">%d</xliff:g> Std."</string>
+    <string name="duration_days_medium_past" msgid="5748156261134344532">"vor <xliff:g id="COUNT">%d</xliff:g> Tg."</string>
+    <string name="duration_years_medium_past" msgid="893797065424596243">"vor <xliff:g id="COUNT">%d</xliff:g> J."</string>
     <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{Vor # Minute}other{Vor # Minuten}}"</string>
     <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{Vor # Stunde}other{Vor # Stunden}}"</string>
     <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{Vor # Tag}other{Vor # Tagen}}"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index b4b70d8..1aae63f 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -1158,38 +1158,22 @@
     <string name="duration_hours_shortest_future" msgid="2979276794547981674">"σε <xliff:g id="COUNT">%d</xliff:g>ώ."</string>
     <string name="duration_days_shortest_future" msgid="3392722163935571543">"σε <xliff:g id="COUNT">%d</xliff:g>η."</string>
     <string name="duration_years_shortest_future" msgid="5537464088352970388">"σε <xliff:g id="COUNT">%d</xliff:g>έτ."</string>
-    <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
-    <skip />
-    <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
-    <skip />
-    <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
-    <skip />
-    <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium (1465359726485910115) -->
-    <skip />
-    <!-- no translation found for duration_days_medium (5994225628248661388) -->
-    <skip />
-    <!-- no translation found for duration_years_medium (734023884353592526) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_past (893797065424596243) -->
-    <skip />
+    <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g> λ. πριν"</string>
+    <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g> ω. πριν"</string>
+    <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g> η. πριν"</string>
+    <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g> χρ. πριν"</string>
+    <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> λ."</string>
+    <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> ω."</string>
+    <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> η."</string>
+    <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> χρ."</string>
+    <string name="duration_minutes_medium_future" msgid="2750894988731934402">"σε <xliff:g id="COUNT">%d</xliff:g> λ."</string>
+    <string name="duration_hours_medium_future" msgid="6050833881463849764">"σε <xliff:g id="COUNT">%d</xliff:g> ω."</string>
+    <string name="duration_days_medium_future" msgid="1700821545602729963">"σε <xliff:g id="COUNT">%d</xliff:g> η."</string>
+    <string name="duration_years_medium_future" msgid="3281018940397120166">"σε <xliff:g id="COUNT">%d</xliff:g> χρ."</string>
+    <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g> λ. πριν"</string>
+    <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g> ω. πριν"</string>
+    <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g> η. πριν"</string>
+    <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g> χρ. πριν"</string>
     <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# λεπτό πριν}other{Πριν από # λεπτά}}"</string>
     <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{Πριν από # ώρα}other{Πριν από # ώρες}}"</string>
     <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{Πριν από # ημέρα}other{Πριν από # ημέρες}}"</string>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index 58037eb..3c5e02a 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -1158,38 +1158,22 @@
     <string name="duration_hours_shortest_future" msgid="2979276794547981674">"in <xliff:g id="COUNT">%d</xliff:g>h"</string>
     <string name="duration_days_shortest_future" msgid="3392722163935571543">"in <xliff:g id="COUNT">%d</xliff:g>d"</string>
     <string name="duration_years_shortest_future" msgid="5537464088352970388">"in <xliff:g id="COUNT">%d</xliff:g> y"</string>
-    <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
-    <skip />
-    <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
-    <skip />
-    <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
-    <skip />
-    <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium (1465359726485910115) -->
-    <skip />
-    <!-- no translation found for duration_days_medium (5994225628248661388) -->
-    <skip />
-    <!-- no translation found for duration_years_medium (734023884353592526) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_past (893797065424596243) -->
-    <skip />
+    <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g>m ago"</string>
+    <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g>h ago"</string>
+    <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g>d ago"</string>
+    <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g>y ago"</string>
+    <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g>min"</string>
+    <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g>hr"</string>
+    <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g>d"</string>
+    <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g>yr"</string>
+    <string name="duration_minutes_medium_future" msgid="2750894988731934402">"in <xliff:g id="COUNT">%d</xliff:g>min"</string>
+    <string name="duration_hours_medium_future" msgid="6050833881463849764">"in <xliff:g id="COUNT">%d</xliff:g>hr"</string>
+    <string name="duration_days_medium_future" msgid="1700821545602729963">"in <xliff:g id="COUNT">%d</xliff:g>d"</string>
+    <string name="duration_years_medium_future" msgid="3281018940397120166">"in <xliff:g id="COUNT">%d</xliff:g>yr"</string>
+    <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g>min ago"</string>
+    <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g>hr ago"</string>
+    <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g>d ago"</string>
+    <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g>yr ago"</string>
     <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# minute ago}other{# minutes ago}}"</string>
     <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# hour ago}other{# hours ago}}"</string>
     <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# day ago}other{# days ago}}"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 43b6ba6..cf02080 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -1158,38 +1158,22 @@
     <string name="duration_hours_shortest_future" msgid="2979276794547981674">"in <xliff:g id="COUNT">%d</xliff:g>h"</string>
     <string name="duration_days_shortest_future" msgid="3392722163935571543">"in <xliff:g id="COUNT">%d</xliff:g>d"</string>
     <string name="duration_years_shortest_future" msgid="5537464088352970388">"in <xliff:g id="COUNT">%d</xliff:g> y"</string>
-    <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
-    <skip />
-    <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
-    <skip />
-    <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
-    <skip />
-    <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium (1465359726485910115) -->
-    <skip />
-    <!-- no translation found for duration_days_medium (5994225628248661388) -->
-    <skip />
-    <!-- no translation found for duration_years_medium (734023884353592526) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_past (893797065424596243) -->
-    <skip />
+    <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g>m ago"</string>
+    <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g>h ago"</string>
+    <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g>d ago"</string>
+    <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g>y ago"</string>
+    <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g>min"</string>
+    <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g>hr"</string>
+    <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g>d"</string>
+    <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g>yr"</string>
+    <string name="duration_minutes_medium_future" msgid="2750894988731934402">"in <xliff:g id="COUNT">%d</xliff:g>min"</string>
+    <string name="duration_hours_medium_future" msgid="6050833881463849764">"in <xliff:g id="COUNT">%d</xliff:g>hr"</string>
+    <string name="duration_days_medium_future" msgid="1700821545602729963">"in <xliff:g id="COUNT">%d</xliff:g>d"</string>
+    <string name="duration_years_medium_future" msgid="3281018940397120166">"in <xliff:g id="COUNT">%d</xliff:g>yr"</string>
+    <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g>min ago"</string>
+    <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g>hr ago"</string>
+    <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g>d ago"</string>
+    <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g>yr ago"</string>
     <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# minute ago}other{# minutes ago}}"</string>
     <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# hour ago}other{# hours ago}}"</string>
     <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# day ago}other{# days ago}}"</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index 1a0f8e4..30b6011 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -1158,38 +1158,22 @@
     <string name="duration_hours_shortest_future" msgid="2979276794547981674">"in <xliff:g id="COUNT">%d</xliff:g>h"</string>
     <string name="duration_days_shortest_future" msgid="3392722163935571543">"in <xliff:g id="COUNT">%d</xliff:g>d"</string>
     <string name="duration_years_shortest_future" msgid="5537464088352970388">"in <xliff:g id="COUNT">%d</xliff:g> y"</string>
-    <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
-    <skip />
-    <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
-    <skip />
-    <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
-    <skip />
-    <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium (1465359726485910115) -->
-    <skip />
-    <!-- no translation found for duration_days_medium (5994225628248661388) -->
-    <skip />
-    <!-- no translation found for duration_years_medium (734023884353592526) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_past (893797065424596243) -->
-    <skip />
+    <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g>m ago"</string>
+    <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g>h ago"</string>
+    <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g>d ago"</string>
+    <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g>y ago"</string>
+    <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g>min"</string>
+    <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g>hr"</string>
+    <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g>d"</string>
+    <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g>yr"</string>
+    <string name="duration_minutes_medium_future" msgid="2750894988731934402">"in <xliff:g id="COUNT">%d</xliff:g>min"</string>
+    <string name="duration_hours_medium_future" msgid="6050833881463849764">"in <xliff:g id="COUNT">%d</xliff:g>hr"</string>
+    <string name="duration_days_medium_future" msgid="1700821545602729963">"in <xliff:g id="COUNT">%d</xliff:g>d"</string>
+    <string name="duration_years_medium_future" msgid="3281018940397120166">"in <xliff:g id="COUNT">%d</xliff:g>yr"</string>
+    <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g>min ago"</string>
+    <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g>hr ago"</string>
+    <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g>d ago"</string>
+    <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g>yr ago"</string>
     <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# minute ago}other{# minutes ago}}"</string>
     <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# hour ago}other{# hours ago}}"</string>
     <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# day ago}other{# days ago}}"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 3c73920..867e8c5 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -1159,38 +1159,22 @@
     <string name="duration_hours_shortest_future" msgid="2979276794547981674">"en <xliff:g id="COUNT">%d</xliff:g> h"</string>
     <string name="duration_days_shortest_future" msgid="3392722163935571543">"en <xliff:g id="COUNT">%d</xliff:g> d"</string>
     <string name="duration_years_shortest_future" msgid="5537464088352970388">"en <xliff:g id="COUNT">%d</xliff:g> años"</string>
-    <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
-    <skip />
-    <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
-    <skip />
-    <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
-    <skip />
-    <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium (1465359726485910115) -->
-    <skip />
-    <!-- no translation found for duration_days_medium (5994225628248661388) -->
-    <skip />
-    <!-- no translation found for duration_years_medium (734023884353592526) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_past (893797065424596243) -->
-    <skip />
+    <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g> min atrás"</string>
+    <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g> h atrás"</string>
+    <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g> d atrás"</string>
+    <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g> años atrás"</string>
+    <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> min"</string>
+    <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> h"</string>
+    <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> d"</string>
+    <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> años"</string>
+    <string name="duration_minutes_medium_future" msgid="2750894988731934402">"en <xliff:g id="COUNT">%d</xliff:g> min"</string>
+    <string name="duration_hours_medium_future" msgid="6050833881463849764">"en <xliff:g id="COUNT">%d</xliff:g> h"</string>
+    <string name="duration_days_medium_future" msgid="1700821545602729963">"en <xliff:g id="COUNT">%d</xliff:g> d"</string>
+    <string name="duration_years_medium_future" msgid="3281018940397120166">"en <xliff:g id="COUNT">%d</xliff:g> año"</string>
+    <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g> min atrás"</string>
+    <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g> h atrás"</string>
+    <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g> d atrás"</string>
+    <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g> años atrás"</string>
     <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{Hace # minuto}many{Hace # minutos}other{Hace # minutos}}"</string>
     <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{Hace # hora}many{Hace # de horas}other{Hace # horas}}"</string>
     <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{Hace # día}many{Hace # de días}other{Hace # días}}"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index a81af9f..1f10c5d 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -1159,38 +1159,22 @@
     <string name="duration_hours_shortest_future" msgid="2979276794547981674">"en <xliff:g id="COUNT">%d</xliff:g>h"</string>
     <string name="duration_days_shortest_future" msgid="3392722163935571543">"en <xliff:g id="COUNT">%d</xliff:g> d"</string>
     <string name="duration_years_shortest_future" msgid="5537464088352970388">"en <xliff:g id="COUNT">%d</xliff:g>a"</string>
-    <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
-    <skip />
-    <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
-    <skip />
-    <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
-    <skip />
-    <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium (1465359726485910115) -->
-    <skip />
-    <!-- no translation found for duration_days_medium (5994225628248661388) -->
-    <skip />
-    <!-- no translation found for duration_years_medium (734023884353592526) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_past (893797065424596243) -->
-    <skip />
+    <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"hace <xliff:g id="COUNT">%d</xliff:g> min"</string>
+    <string name="duration_hours_shortest_past" msgid="2098397414186628489">"hace <xliff:g id="COUNT">%d</xliff:g> h"</string>
+    <string name="duration_days_shortest_past" msgid="1832006037955897625">"hace <xliff:g id="COUNT">%d</xliff:g> d"</string>
+    <string name="duration_years_shortest_past" msgid="6168256514200469291">"hace <xliff:g id="COUNT">%d</xliff:g> a"</string>
+    <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> min"</string>
+    <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> h"</string>
+    <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> d"</string>
+    <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> a"</string>
+    <string name="duration_minutes_medium_future" msgid="2750894988731934402">"en <xliff:g id="COUNT">%d</xliff:g> min"</string>
+    <string name="duration_hours_medium_future" msgid="6050833881463849764">"en <xliff:g id="COUNT">%d</xliff:g> h"</string>
+    <string name="duration_days_medium_future" msgid="1700821545602729963">"en <xliff:g id="COUNT">%d</xliff:g> d"</string>
+    <string name="duration_years_medium_future" msgid="3281018940397120166">"en <xliff:g id="COUNT">%d</xliff:g> a"</string>
+    <string name="duration_minutes_medium_past" msgid="7400424340181947714">"hace <xliff:g id="COUNT">%d</xliff:g> min"</string>
+    <string name="duration_hours_medium_past" msgid="6709441336035202617">"hace <xliff:g id="COUNT">%d</xliff:g> h"</string>
+    <string name="duration_days_medium_past" msgid="5748156261134344532">"hace <xliff:g id="COUNT">%d</xliff:g> d"</string>
+    <string name="duration_years_medium_past" msgid="893797065424596243">"hace <xliff:g id="COUNT">%d</xliff:g> a"</string>
     <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{Hace # minuto}many{Hace # minutos}other{Hace # minutos}}"</string>
     <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{Hace # hora}many{Hace # horas}other{Hace # horas}}"</string>
     <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{Hace # día}many{Hace # días}other{Hace # días}}"</string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index c5cadcf..58f5f20 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -1158,38 +1158,22 @@
     <string name="duration_hours_shortest_future" msgid="2979276794547981674">"<xliff:g id="COUNT">%d</xliff:g> h pärast"</string>
     <string name="duration_days_shortest_future" msgid="3392722163935571543">"<xliff:g id="COUNT">%d</xliff:g> p pärast"</string>
     <string name="duration_years_shortest_future" msgid="5537464088352970388">"<xliff:g id="COUNT">%d</xliff:g> a pärast"</string>
-    <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
-    <skip />
-    <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
-    <skip />
-    <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
-    <skip />
-    <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium (1465359726485910115) -->
-    <skip />
-    <!-- no translation found for duration_days_medium (5994225628248661388) -->
-    <skip />
-    <!-- no translation found for duration_years_medium (734023884353592526) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_past (893797065424596243) -->
-    <skip />
+    <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g> min tagasi"</string>
+    <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g> h tagasi"</string>
+    <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g> p tagasi"</string>
+    <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g> a tagasi"</string>
+    <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> min"</string>
+    <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> h"</string>
+    <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> p"</string>
+    <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> a"</string>
+    <string name="duration_minutes_medium_future" msgid="2750894988731934402">"<xliff:g id="COUNT">%d</xliff:g> min pärast"</string>
+    <string name="duration_hours_medium_future" msgid="6050833881463849764">"<xliff:g id="COUNT">%d</xliff:g> h pärast"</string>
+    <string name="duration_days_medium_future" msgid="1700821545602729963">"<xliff:g id="COUNT">%d</xliff:g> p pärast"</string>
+    <string name="duration_years_medium_future" msgid="3281018940397120166">"<xliff:g id="COUNT">%d</xliff:g> a pärast"</string>
+    <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g> min tagasi"</string>
+    <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g> h tagasi"</string>
+    <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g> p tagasi"</string>
+    <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g> a tagasi"</string>
     <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# minut tagasi}other{# minutit tagasi}}"</string>
     <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# tund tagasi}other{# tundi tagasi}}"</string>
     <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# päev tagasi}other{# päeva tagasi}}"</string>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index a0b2cc3..ab3b66b 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -1158,38 +1158,22 @@
     <string name="duration_hours_shortest_future" msgid="2979276794547981674">"<xliff:g id="COUNT">%d</xliff:g> h barru"</string>
     <string name="duration_days_shortest_future" msgid="3392722163935571543">"<xliff:g id="COUNT">%d</xliff:g> eg. barru"</string>
     <string name="duration_years_shortest_future" msgid="5537464088352970388">"<xliff:g id="COUNT">%d</xliff:g> ur. barru"</string>
-    <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
-    <skip />
-    <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
-    <skip />
-    <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
-    <skip />
-    <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium (1465359726485910115) -->
-    <skip />
-    <!-- no translation found for duration_days_medium (5994225628248661388) -->
-    <skip />
-    <!-- no translation found for duration_years_medium (734023884353592526) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_past (893797065424596243) -->
-    <skip />
+    <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"Duela <xliff:g id="COUNT">%d</xliff:g> min"</string>
+    <string name="duration_hours_shortest_past" msgid="2098397414186628489">"Duela <xliff:g id="COUNT">%d</xliff:g> h"</string>
+    <string name="duration_days_shortest_past" msgid="1832006037955897625">"Duela <xliff:g id="COUNT">%d</xliff:g> egun"</string>
+    <string name="duration_years_shortest_past" msgid="6168256514200469291">"Duela <xliff:g id="COUNT">%d</xliff:g> urte"</string>
+    <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> min"</string>
+    <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> h"</string>
+    <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> egun"</string>
+    <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> urte"</string>
+    <string name="duration_minutes_medium_future" msgid="2750894988731934402">"<xliff:g id="COUNT">%d</xliff:g> min barru"</string>
+    <string name="duration_hours_medium_future" msgid="6050833881463849764">"<xliff:g id="COUNT">%d</xliff:g> h barru"</string>
+    <string name="duration_days_medium_future" msgid="1700821545602729963">"<xliff:g id="COUNT">%d</xliff:g> egun barru"</string>
+    <string name="duration_years_medium_future" msgid="3281018940397120166">"<xliff:g id="COUNT">%d</xliff:g> urte barru"</string>
+    <string name="duration_minutes_medium_past" msgid="7400424340181947714">"Duela <xliff:g id="COUNT">%d</xliff:g> min"</string>
+    <string name="duration_hours_medium_past" msgid="6709441336035202617">"Duela <xliff:g id="COUNT">%d</xliff:g> h"</string>
+    <string name="duration_days_medium_past" msgid="5748156261134344532">"Duela <xliff:g id="COUNT">%d</xliff:g> egun"</string>
+    <string name="duration_years_medium_past" msgid="893797065424596243">"Duela <xliff:g id="COUNT">%d</xliff:g> urte"</string>
     <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{Duela # minutu}other{Duela # minutu}}"</string>
     <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{Duela # ordu}other{Duela # ordu}}"</string>
     <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{Duela # egun}other{Duela # egun}}"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 4d3f1a3..954f6ba 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -1158,38 +1158,22 @@
     <string name="duration_hours_shortest_future" msgid="2979276794547981674">"تا <xliff:g id="COUNT">%d</xliff:g> ساعت دیگر"</string>
     <string name="duration_days_shortest_future" msgid="3392722163935571543">"تا <xliff:g id="COUNT">%d</xliff:g> روز دیگر"</string>
     <string name="duration_years_shortest_future" msgid="5537464088352970388">"تا <xliff:g id="COUNT">%d</xliff:g> سال دیگر"</string>
-    <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
-    <skip />
-    <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
-    <skip />
-    <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
-    <skip />
-    <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium (1465359726485910115) -->
-    <skip />
-    <!-- no translation found for duration_days_medium (5994225628248661388) -->
-    <skip />
-    <!-- no translation found for duration_years_medium (734023884353592526) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_past (893797065424596243) -->
-    <skip />
+    <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"‫<xliff:g id="COUNT">%d</xliff:g> دقیقه پیش"</string>
+    <string name="duration_hours_shortest_past" msgid="2098397414186628489">"‫<xliff:g id="COUNT">%d</xliff:g> ساعت پیش"</string>
+    <string name="duration_days_shortest_past" msgid="1832006037955897625">"‫<xliff:g id="COUNT">%d</xliff:g> روز پیش"</string>
+    <string name="duration_years_shortest_past" msgid="6168256514200469291">"‫<xliff:g id="COUNT">%d</xliff:g> سال پیش"</string>
+    <string name="duration_minutes_medium" msgid="5891933490342643944">"‫<xliff:g id="COUNT">%d</xliff:g> دقیقه"</string>
+    <string name="duration_hours_medium" msgid="1465359726485910115">"‫<xliff:g id="COUNT">%d</xliff:g> ساعت"</string>
+    <string name="duration_days_medium" msgid="5994225628248661388">"‫<xliff:g id="COUNT">%d</xliff:g> روز"</string>
+    <string name="duration_years_medium" msgid="734023884353592526">"‫<xliff:g id="COUNT">%d</xliff:g> سال"</string>
+    <string name="duration_minutes_medium_future" msgid="2750894988731934402">"‫<xliff:g id="COUNT">%d</xliff:g> دقیقه دیگر"</string>
+    <string name="duration_hours_medium_future" msgid="6050833881463849764">"‫<xliff:g id="COUNT">%d</xliff:g> ساعت دیگر"</string>
+    <string name="duration_days_medium_future" msgid="1700821545602729963">"‫<xliff:g id="COUNT">%d</xliff:g> روز دیگر"</string>
+    <string name="duration_years_medium_future" msgid="3281018940397120166">"‫<xliff:g id="COUNT">%d</xliff:g> سال دیگر"</string>
+    <string name="duration_minutes_medium_past" msgid="7400424340181947714">"‫<xliff:g id="COUNT">%d</xliff:g> دقیقه پیش"</string>
+    <string name="duration_hours_medium_past" msgid="6709441336035202617">"‫<xliff:g id="COUNT">%d</xliff:g> ساعت پیش"</string>
+    <string name="duration_days_medium_past" msgid="5748156261134344532">"‫<xliff:g id="COUNT">%d</xliff:g> روز پیش"</string>
+    <string name="duration_years_medium_past" msgid="893797065424596243">"‫<xliff:g id="COUNT">%d</xliff:g> سال پیش"</string>
     <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# دقیقه قبل}one{# دقیقه قبل}other{# دقیقه قبل}}"</string>
     <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# ساعت قبل}one{# ساعت قبل}other{# ساعت قبل}}"</string>
     <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# روز قبل}one{# روز قبل}other{# روز قبل}}"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 2f57750..96609c9 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -1158,38 +1158,22 @@
     <string name="duration_hours_shortest_future" msgid="2979276794547981674">"<xliff:g id="COUNT">%d</xliff:g> h:n päästä"</string>
     <string name="duration_days_shortest_future" msgid="3392722163935571543">"<xliff:g id="COUNT">%d</xliff:g> pv:n päästä"</string>
     <string name="duration_years_shortest_future" msgid="5537464088352970388">"<xliff:g id="COUNT">%d</xliff:g> v:n päästä"</string>
-    <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
-    <skip />
-    <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
-    <skip />
-    <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
-    <skip />
-    <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium (1465359726485910115) -->
-    <skip />
-    <!-- no translation found for duration_days_medium (5994225628248661388) -->
-    <skip />
-    <!-- no translation found for duration_years_medium (734023884353592526) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_past (893797065424596243) -->
-    <skip />
+    <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g> min sitten"</string>
+    <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g> h sitten"</string>
+    <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g> pv sitten"</string>
+    <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g> v sitten"</string>
+    <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> min"</string>
+    <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> h"</string>
+    <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> pv"</string>
+    <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> v"</string>
+    <string name="duration_minutes_medium_future" msgid="2750894988731934402">"<xliff:g id="COUNT">%d</xliff:g> min päästä"</string>
+    <string name="duration_hours_medium_future" msgid="6050833881463849764">"<xliff:g id="COUNT">%d</xliff:g> h:n päästä"</string>
+    <string name="duration_days_medium_future" msgid="1700821545602729963">"<xliff:g id="COUNT">%d</xliff:g> pv:n päästä"</string>
+    <string name="duration_years_medium_future" msgid="3281018940397120166">"<xliff:g id="COUNT">%d</xliff:g> v:n päästä"</string>
+    <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g> min sitten"</string>
+    <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g> h sitten"</string>
+    <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g> pv sitten"</string>
+    <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g> v sitten"</string>
     <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# minuutti sitten}other{# minuuttia sitten}}"</string>
     <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# tunti sitten}other{# tuntia sitten}}"</string>
     <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# päivä sitten}other{# päivää sitten}}"</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index a87fd67..50a7696 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -1159,38 +1159,22 @@
     <string name="duration_hours_shortest_future" msgid="2979276794547981674">"dans <xliff:g id="COUNT">%d</xliff:g> h"</string>
     <string name="duration_days_shortest_future" msgid="3392722163935571543">"dans <xliff:g id="COUNT">%d</xliff:g> j"</string>
     <string name="duration_years_shortest_future" msgid="5537464088352970388">"dans <xliff:g id="COUNT">%d</xliff:g> a"</string>
-    <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
-    <skip />
-    <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
-    <skip />
-    <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
-    <skip />
-    <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium (1465359726485910115) -->
-    <skip />
-    <!-- no translation found for duration_days_medium (5994225628248661388) -->
-    <skip />
-    <!-- no translation found for duration_years_medium (734023884353592526) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_past (893797065424596243) -->
-    <skip />
+    <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"il y a <xliff:g id="COUNT">%d</xliff:g> min"</string>
+    <string name="duration_hours_shortest_past" msgid="2098397414186628489">"il y a <xliff:g id="COUNT">%d</xliff:g> h"</string>
+    <string name="duration_days_shortest_past" msgid="1832006037955897625">"il y a <xliff:g id="COUNT">%d</xliff:g> j"</string>
+    <string name="duration_years_shortest_past" msgid="6168256514200469291">"il y a <xliff:g id="COUNT">%d</xliff:g> ans"</string>
+    <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> min"</string>
+    <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> h"</string>
+    <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> j"</string>
+    <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> ans"</string>
+    <string name="duration_minutes_medium_future" msgid="2750894988731934402">"dans <xliff:g id="COUNT">%d</xliff:g> min"</string>
+    <string name="duration_hours_medium_future" msgid="6050833881463849764">"dans <xliff:g id="COUNT">%d</xliff:g> h"</string>
+    <string name="duration_days_medium_future" msgid="1700821545602729963">"dans <xliff:g id="COUNT">%d</xliff:g> j"</string>
+    <string name="duration_years_medium_future" msgid="3281018940397120166">"dans <xliff:g id="COUNT">%d</xliff:g> ans"</string>
+    <string name="duration_minutes_medium_past" msgid="7400424340181947714">"il y a <xliff:g id="COUNT">%d</xliff:g> min"</string>
+    <string name="duration_hours_medium_past" msgid="6709441336035202617">"il y a <xliff:g id="COUNT">%d</xliff:g> h"</string>
+    <string name="duration_days_medium_past" msgid="5748156261134344532">"il y a <xliff:g id="COUNT">%d</xliff:g> j"</string>
+    <string name="duration_years_medium_past" msgid="893797065424596243">"il y a <xliff:g id="COUNT">%d</xliff:g> ans"</string>
     <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{Il y a # minute}one{Il y a # minute}many{Il y a # minutes}other{Il y a # minutes}}"</string>
     <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{Il y a # heure}one{Il y a # heure}many{Il y a # heures}other{Il y a # heures}}"</string>
     <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{Il y a # jour}one{Il y a # jour}many{Il y a # jours}other{Il y a # jours}}"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index ba09a53..a72444d 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -1159,38 +1159,22 @@
     <string name="duration_hours_shortest_future" msgid="2979276794547981674">"dans <xliff:g id="COUNT">%d</xliff:g> h"</string>
     <string name="duration_days_shortest_future" msgid="3392722163935571543">"dans <xliff:g id="COUNT">%d</xliff:g> j"</string>
     <string name="duration_years_shortest_future" msgid="5537464088352970388">"dans <xliff:g id="COUNT">%d</xliff:g> an"</string>
-    <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
-    <skip />
-    <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
-    <skip />
-    <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
-    <skip />
-    <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium (1465359726485910115) -->
-    <skip />
-    <!-- no translation found for duration_days_medium (5994225628248661388) -->
-    <skip />
-    <!-- no translation found for duration_years_medium (734023884353592526) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_past (893797065424596243) -->
-    <skip />
+    <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"il y a <xliff:g id="COUNT">%d</xliff:g> min"</string>
+    <string name="duration_hours_shortest_past" msgid="2098397414186628489">"il y a <xliff:g id="COUNT">%d</xliff:g> h"</string>
+    <string name="duration_days_shortest_past" msgid="1832006037955897625">"il y a <xliff:g id="COUNT">%d</xliff:g> j"</string>
+    <string name="duration_years_shortest_past" msgid="6168256514200469291">"il y a <xliff:g id="COUNT">%d</xliff:g> an(s)"</string>
+    <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> min"</string>
+    <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> h"</string>
+    <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> j"</string>
+    <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> an(s)"</string>
+    <string name="duration_minutes_medium_future" msgid="2750894988731934402">"dans <xliff:g id="COUNT">%d</xliff:g> min"</string>
+    <string name="duration_hours_medium_future" msgid="6050833881463849764">"dans <xliff:g id="COUNT">%d</xliff:g> h"</string>
+    <string name="duration_days_medium_future" msgid="1700821545602729963">"dans <xliff:g id="COUNT">%d</xliff:g> j"</string>
+    <string name="duration_years_medium_future" msgid="3281018940397120166">"dans <xliff:g id="COUNT">%d</xliff:g> an(s)"</string>
+    <string name="duration_minutes_medium_past" msgid="7400424340181947714">"il y a <xliff:g id="COUNT">%d</xliff:g> min"</string>
+    <string name="duration_hours_medium_past" msgid="6709441336035202617">"il y a <xliff:g id="COUNT">%d</xliff:g> h"</string>
+    <string name="duration_days_medium_past" msgid="5748156261134344532">"il y a <xliff:g id="COUNT">%d</xliff:g> j"</string>
+    <string name="duration_years_medium_past" msgid="893797065424596243">"il y a <xliff:g id="COUNT">%d</xliff:g> an(s)"</string>
     <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{Il y a # minute}one{Il y a # minute}many{Il y a # minutes}other{Il y a # minutes}}"</string>
     <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{Il y a # heure}one{Il y a # heure}many{Il y a # heures}other{Il y a # heures}}"</string>
     <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{Il y a # jour}one{Il y a # jour}many{Il y a # jours}other{Il y a # jours}}"</string>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index c46436f..27454b9 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -1158,38 +1158,22 @@
     <string name="duration_hours_shortest_future" msgid="2979276794547981674">"en <xliff:g id="COUNT">%d</xliff:g> h"</string>
     <string name="duration_days_shortest_future" msgid="3392722163935571543">"en <xliff:g id="COUNT">%d</xliff:g> d"</string>
     <string name="duration_years_shortest_future" msgid="5537464088352970388">"en <xliff:g id="COUNT">%d</xliff:g> a"</string>
-    <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
-    <skip />
-    <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
-    <skip />
-    <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
-    <skip />
-    <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium (1465359726485910115) -->
-    <skip />
-    <!-- no translation found for duration_days_medium (5994225628248661388) -->
-    <skip />
-    <!-- no translation found for duration_years_medium (734023884353592526) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_past (893797065424596243) -->
-    <skip />
+    <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"hai <xliff:g id="COUNT">%d</xliff:g> min"</string>
+    <string name="duration_hours_shortest_past" msgid="2098397414186628489">"hai <xliff:g id="COUNT">%d</xliff:g> h"</string>
+    <string name="duration_days_shortest_past" msgid="1832006037955897625">"hai <xliff:g id="COUNT">%d</xliff:g> d"</string>
+    <string name="duration_years_shortest_past" msgid="6168256514200469291">"hai <xliff:g id="COUNT">%d</xliff:g> a."</string>
+    <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> min"</string>
+    <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> h"</string>
+    <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> d"</string>
+    <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> a."</string>
+    <string name="duration_minutes_medium_future" msgid="2750894988731934402">"en <xliff:g id="COUNT">%d</xliff:g> min"</string>
+    <string name="duration_hours_medium_future" msgid="6050833881463849764">"en <xliff:g id="COUNT">%d</xliff:g> h"</string>
+    <string name="duration_days_medium_future" msgid="1700821545602729963">"en <xliff:g id="COUNT">%d</xliff:g> d"</string>
+    <string name="duration_years_medium_future" msgid="3281018940397120166">"en <xliff:g id="COUNT">%d</xliff:g> a"</string>
+    <string name="duration_minutes_medium_past" msgid="7400424340181947714">"hai <xliff:g id="COUNT">%d</xliff:g> min"</string>
+    <string name="duration_hours_medium_past" msgid="6709441336035202617">"hai <xliff:g id="COUNT">%d</xliff:g> h"</string>
+    <string name="duration_days_medium_past" msgid="5748156261134344532">"hai <xliff:g id="COUNT">%d</xliff:g> d"</string>
+    <string name="duration_years_medium_past" msgid="893797065424596243">"hai <xliff:g id="COUNT">%d</xliff:g> a."</string>
     <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{Hai # minuto}other{Hai # minutos}}"</string>
     <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{Hai # hora}other{Hai # horas}}"</string>
     <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{Hai # día}other{Hai # días}}"</string>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index 89f5a59..4e4c9ff 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -1158,38 +1158,22 @@
     <string name="duration_hours_shortest_future" msgid="2979276794547981674">"<xliff:g id="COUNT">%d</xliff:g> કલાકમાં"</string>
     <string name="duration_days_shortest_future" msgid="3392722163935571543">"<xliff:g id="COUNT">%d</xliff:g> દિવસમાં"</string>
     <string name="duration_years_shortest_future" msgid="5537464088352970388">"<xliff:g id="COUNT">%d</xliff:g> વર્ષમાં"</string>
-    <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
-    <skip />
-    <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
-    <skip />
-    <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
-    <skip />
-    <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium (1465359726485910115) -->
-    <skip />
-    <!-- no translation found for duration_days_medium (5994225628248661388) -->
-    <skip />
-    <!-- no translation found for duration_years_medium (734023884353592526) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_past (893797065424596243) -->
-    <skip />
+    <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g> મિનિટ પહેલાં"</string>
+    <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g> કલાક પહેલાં"</string>
+    <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g> દિવસ પહેલાં"</string>
+    <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g> વર્ષ પહેલાં"</string>
+    <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> મિનિટ"</string>
+    <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> કલાક"</string>
+    <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> દિવસ"</string>
+    <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> વર્ષ"</string>
+    <string name="duration_minutes_medium_future" msgid="2750894988731934402">"<xliff:g id="COUNT">%d</xliff:g> મિનિટમાં"</string>
+    <string name="duration_hours_medium_future" msgid="6050833881463849764">"<xliff:g id="COUNT">%d</xliff:g> કલાકમાં"</string>
+    <string name="duration_days_medium_future" msgid="1700821545602729963">"<xliff:g id="COUNT">%d</xliff:g> દિવસમાં"</string>
+    <string name="duration_years_medium_future" msgid="3281018940397120166">"<xliff:g id="COUNT">%d</xliff:g> વર્ષમાં"</string>
+    <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g> મિનિટ પહેલાં"</string>
+    <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g> કલાક પહેલાં"</string>
+    <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g> દિવસ પહેલાં"</string>
+    <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g> વર્ષ પહેલાં"</string>
     <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# મિનિટ પહેલાં}one{# મિનિટ પહેલાં}other{# મિનિટ પહેલાં}}"</string>
     <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# કલાક પહેલાં}one{# કલાક પહેલાં}other{# કલાક પહેલાં}}"</string>
     <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# દિવસ પહેલાં}one{# દિવસ પહેલાં}other{# દિવસ પહેલાં}}"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 4bdfa6b..624d7a5 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -1158,38 +1158,22 @@
     <string name="duration_hours_shortest_future" msgid="2979276794547981674">"<xliff:g id="COUNT">%d</xliff:g> घंटे में"</string>
     <string name="duration_days_shortest_future" msgid="3392722163935571543">"<xliff:g id="COUNT">%d</xliff:g> दिन में"</string>
     <string name="duration_years_shortest_future" msgid="5537464088352970388">"<xliff:g id="COUNT">%d</xliff:g> साल में"</string>
-    <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
-    <skip />
-    <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
-    <skip />
-    <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
-    <skip />
-    <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium (1465359726485910115) -->
-    <skip />
-    <!-- no translation found for duration_days_medium (5994225628248661388) -->
-    <skip />
-    <!-- no translation found for duration_years_medium (734023884353592526) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_past (893797065424596243) -->
-    <skip />
+    <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g> मिनट पहले"</string>
+    <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g> घंटे पहले"</string>
+    <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g> दिन पहले"</string>
+    <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g> साल पहले"</string>
+    <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> मिनट"</string>
+    <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> घंटे"</string>
+    <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> दिन"</string>
+    <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> साल"</string>
+    <string name="duration_minutes_medium_future" msgid="2750894988731934402">"<xliff:g id="COUNT">%d</xliff:g> मिनट में"</string>
+    <string name="duration_hours_medium_future" msgid="6050833881463849764">"<xliff:g id="COUNT">%d</xliff:g> घंटे में"</string>
+    <string name="duration_days_medium_future" msgid="1700821545602729963">"<xliff:g id="COUNT">%d</xliff:g> दिन में"</string>
+    <string name="duration_years_medium_future" msgid="3281018940397120166">"<xliff:g id="COUNT">%d</xliff:g> साल में"</string>
+    <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g> मिनट पहले"</string>
+    <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g> घंटे पहले"</string>
+    <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g> दिन पहले"</string>
+    <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g> साल पहले"</string>
     <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# मिनट पहले}one{# मिनट पहले}other{# मिनट पहले}}"</string>
     <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# घंटा पहले}one{# घंटा पहले}other{# घंटे पहले}}"</string>
     <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# दिन पहले}one{# दिन पहले}other{# दिन पहले}}"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index cd7d023..3429c14 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -1159,38 +1159,22 @@
     <string name="duration_hours_shortest_future" msgid="2979276794547981674">"za <xliff:g id="COUNT">%d</xliff:g> h"</string>
     <string name="duration_days_shortest_future" msgid="3392722163935571543">"za <xliff:g id="COUNT">%d</xliff:g> d"</string>
     <string name="duration_years_shortest_future" msgid="5537464088352970388">"za <xliff:g id="COUNT">%d</xliff:g> g."</string>
-    <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
-    <skip />
-    <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
-    <skip />
-    <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
-    <skip />
-    <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium (1465359726485910115) -->
-    <skip />
-    <!-- no translation found for duration_days_medium (5994225628248661388) -->
-    <skip />
-    <!-- no translation found for duration_years_medium (734023884353592526) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_past (893797065424596243) -->
-    <skip />
+    <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"prije <xliff:g id="COUNT">%d</xliff:g> min"</string>
+    <string name="duration_hours_shortest_past" msgid="2098397414186628489">"prije <xliff:g id="COUNT">%d</xliff:g> h"</string>
+    <string name="duration_days_shortest_past" msgid="1832006037955897625">"prije <xliff:g id="COUNT">%d</xliff:g> dana"</string>
+    <string name="duration_years_shortest_past" msgid="6168256514200469291">"prije <xliff:g id="COUNT">%d</xliff:g> god."</string>
+    <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> min"</string>
+    <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> h"</string>
+    <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> d"</string>
+    <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> god."</string>
+    <string name="duration_minutes_medium_future" msgid="2750894988731934402">"za <xliff:g id="COUNT">%d</xliff:g> min"</string>
+    <string name="duration_hours_medium_future" msgid="6050833881463849764">"za <xliff:g id="COUNT">%d</xliff:g> h"</string>
+    <string name="duration_days_medium_future" msgid="1700821545602729963">"za <xliff:g id="COUNT">%d</xliff:g> d"</string>
+    <string name="duration_years_medium_future" msgid="3281018940397120166">"za <xliff:g id="COUNT">%d</xliff:g> god."</string>
+    <string name="duration_minutes_medium_past" msgid="7400424340181947714">"prije <xliff:g id="COUNT">%d</xliff:g> min"</string>
+    <string name="duration_hours_medium_past" msgid="6709441336035202617">"prije <xliff:g id="COUNT">%d</xliff:g> h"</string>
+    <string name="duration_days_medium_past" msgid="5748156261134344532">"prije <xliff:g id="COUNT">%d</xliff:g> dana"</string>
+    <string name="duration_years_medium_past" msgid="893797065424596243">"prije <xliff:g id="COUNT">%d</xliff:g> god."</string>
     <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{Prije # min}one{Prije # min}few{Prije # min}other{Prije # min}}"</string>
     <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{Prije # h}one{Prije # h}few{Prije # h}other{Prije # h}}"</string>
     <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{Prije # dan}one{Prije # dan}few{Prije # dana}other{Prije # dana}}"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index ddc8762..2d46a3c 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -1158,38 +1158,22 @@
     <string name="duration_hours_shortest_future" msgid="2979276794547981674">"<xliff:g id="COUNT">%d</xliff:g> ó múlva"</string>
     <string name="duration_days_shortest_future" msgid="3392722163935571543">"<xliff:g id="COUNT">%d</xliff:g> n múlva"</string>
     <string name="duration_years_shortest_future" msgid="5537464088352970388">"<xliff:g id="COUNT">%d</xliff:g> é múlva"</string>
-    <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
-    <skip />
-    <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
-    <skip />
-    <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
-    <skip />
-    <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium (1465359726485910115) -->
-    <skip />
-    <!-- no translation found for duration_days_medium (5994225628248661388) -->
-    <skip />
-    <!-- no translation found for duration_years_medium (734023884353592526) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_past (893797065424596243) -->
-    <skip />
+    <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g> perce"</string>
+    <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g> órája"</string>
+    <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g> napja"</string>
+    <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g> éve"</string>
+    <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> perc"</string>
+    <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> óra"</string>
+    <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> n"</string>
+    <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> év"</string>
+    <string name="duration_minutes_medium_future" msgid="2750894988731934402">"<xliff:g id="COUNT">%d</xliff:g> perc múlva"</string>
+    <string name="duration_hours_medium_future" msgid="6050833881463849764">"<xliff:g id="COUNT">%d</xliff:g> ó múlva"</string>
+    <string name="duration_days_medium_future" msgid="1700821545602729963">"<xliff:g id="COUNT">%d</xliff:g> n múlva"</string>
+    <string name="duration_years_medium_future" msgid="3281018940397120166">"<xliff:g id="COUNT">%d</xliff:g> év múlva"</string>
+    <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g> perce"</string>
+    <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g> órája"</string>
+    <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g> napja"</string>
+    <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g> éve"</string>
     <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# perce}other{# perce}}"</string>
     <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# órája}other{# órája}}"</string>
     <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# napja}other{# napja}}"</string>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index 3e961e7..82d141f 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -1158,38 +1158,22 @@
     <string name="duration_hours_shortest_future" msgid="2979276794547981674">"<xliff:g id="COUNT">%d</xliff:g> ժամից"</string>
     <string name="duration_days_shortest_future" msgid="3392722163935571543">"<xliff:g id="COUNT">%d</xliff:g> օրից"</string>
     <string name="duration_years_shortest_future" msgid="5537464088352970388">"<xliff:g id="COUNT">%d</xliff:g> տարուց"</string>
-    <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
-    <skip />
-    <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
-    <skip />
-    <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
-    <skip />
-    <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium (1465359726485910115) -->
-    <skip />
-    <!-- no translation found for duration_days_medium (5994225628248661388) -->
-    <skip />
-    <!-- no translation found for duration_years_medium (734023884353592526) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_past (893797065424596243) -->
-    <skip />
+    <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g> ր առաջ"</string>
+    <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g> ժ առաջ"</string>
+    <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g> օր առաջ"</string>
+    <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g> տ առաջ"</string>
+    <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> րոպե"</string>
+    <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> ժ"</string>
+    <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> օր"</string>
+    <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> տ"</string>
+    <string name="duration_minutes_medium_future" msgid="2750894988731934402">"<xliff:g id="COUNT">%d</xliff:g> րոպեից"</string>
+    <string name="duration_hours_medium_future" msgid="6050833881463849764">"<xliff:g id="COUNT">%d</xliff:g> ժամից"</string>
+    <string name="duration_days_medium_future" msgid="1700821545602729963">"<xliff:g id="COUNT">%d</xliff:g> օրից"</string>
+    <string name="duration_years_medium_future" msgid="3281018940397120166">"<xliff:g id="COUNT">%d</xliff:g> տարուց"</string>
+    <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g> ր առաջ"</string>
+    <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g> ժ առաջ"</string>
+    <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g> օր առաջ"</string>
+    <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g> տ առաջ"</string>
     <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# րոպե առաջ}one{# րոպե առաջ}other{# րոպե առաջ}}"</string>
     <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# ժամ առաջ}one{# ժամ առաջ}other{# ժամ առաջ}}"</string>
     <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# օր առաջ}one{# օր առաջ}other{# օր առաջ}}"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 594c173..63bc9cf 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -1158,38 +1158,22 @@
     <string name="duration_hours_shortest_future" msgid="2979276794547981674">"dalam <xliff:g id="COUNT">%d</xliff:g> j"</string>
     <string name="duration_days_shortest_future" msgid="3392722163935571543">"dalam <xliff:g id="COUNT">%d</xliff:g> h"</string>
     <string name="duration_years_shortest_future" msgid="5537464088352970388">"dalam <xliff:g id="COUNT">%d</xliff:g> t"</string>
-    <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
-    <skip />
-    <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
-    <skip />
-    <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
-    <skip />
-    <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium (1465359726485910115) -->
-    <skip />
-    <!-- no translation found for duration_days_medium (5994225628248661388) -->
-    <skip />
-    <!-- no translation found for duration_years_medium (734023884353592526) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_past (893797065424596243) -->
-    <skip />
+    <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g> menit lalu"</string>
+    <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g> jam lalu"</string>
+    <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g> hr lalu"</string>
+    <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g> tahun lalu"</string>
+    <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> mnt"</string>
+    <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> jam"</string>
+    <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> hari"</string>
+    <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> tahun"</string>
+    <string name="duration_minutes_medium_future" msgid="2750894988731934402">"dalam <xliff:g id="COUNT">%d</xliff:g> menit"</string>
+    <string name="duration_hours_medium_future" msgid="6050833881463849764">"dalam <xliff:g id="COUNT">%d</xliff:g> jam"</string>
+    <string name="duration_days_medium_future" msgid="1700821545602729963">"dalam <xliff:g id="COUNT">%d</xliff:g> hari"</string>
+    <string name="duration_years_medium_future" msgid="3281018940397120166">"dalam <xliff:g id="COUNT">%d</xliff:g> tahun"</string>
+    <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g> menit lalu"</string>
+    <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g> jam lalu"</string>
+    <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g> hr lalu"</string>
+    <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g> tahun lalu"</string>
     <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# menit lalu}other{# menit lalu}}"</string>
     <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# jam lalu}other{# jam lalu}}"</string>
     <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# hari lalu}other{# hari lalu}}"</string>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index 0242fa3..1b43099 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -1158,38 +1158,22 @@
     <string name="duration_hours_shortest_future" msgid="2979276794547981674">"eftir <xliff:g id="COUNT">%d</xliff:g> klst."</string>
     <string name="duration_days_shortest_future" msgid="3392722163935571543">"eftir <xliff:g id="COUNT">%d</xliff:g> d."</string>
     <string name="duration_years_shortest_future" msgid="5537464088352970388">"eftir <xliff:g id="COUNT">%d</xliff:g> ár"</string>
-    <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
-    <skip />
-    <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
-    <skip />
-    <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
-    <skip />
-    <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium (1465359726485910115) -->
-    <skip />
-    <!-- no translation found for duration_days_medium (5994225628248661388) -->
-    <skip />
-    <!-- no translation found for duration_years_medium (734023884353592526) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_past (893797065424596243) -->
-    <skip />
+    <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"fyrir <xliff:g id="COUNT">%d</xliff:g> mín."</string>
+    <string name="duration_hours_shortest_past" msgid="2098397414186628489">"fyrir <xliff:g id="COUNT">%d</xliff:g> klst."</string>
+    <string name="duration_days_shortest_past" msgid="1832006037955897625">"fyrir <xliff:g id="COUNT">%d</xliff:g> d."</string>
+    <string name="duration_years_shortest_past" msgid="6168256514200469291">"fyrir <xliff:g id="COUNT">%d</xliff:g> á."</string>
+    <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> mín."</string>
+    <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> klst."</string>
+    <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> d."</string>
+    <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> ár"</string>
+    <string name="duration_minutes_medium_future" msgid="2750894988731934402">"eftir <xliff:g id="COUNT">%d</xliff:g> mín."</string>
+    <string name="duration_hours_medium_future" msgid="6050833881463849764">"eftir <xliff:g id="COUNT">%d</xliff:g> klst."</string>
+    <string name="duration_days_medium_future" msgid="1700821545602729963">"eftir <xliff:g id="COUNT">%d</xliff:g> d."</string>
+    <string name="duration_years_medium_future" msgid="3281018940397120166">"eftir <xliff:g id="COUNT">%d</xliff:g> ár"</string>
+    <string name="duration_minutes_medium_past" msgid="7400424340181947714">"fyrir <xliff:g id="COUNT">%d</xliff:g> mín."</string>
+    <string name="duration_hours_medium_past" msgid="6709441336035202617">"fyrir <xliff:g id="COUNT">%d</xliff:g> klst."</string>
+    <string name="duration_days_medium_past" msgid="5748156261134344532">"fyrir <xliff:g id="COUNT">%d</xliff:g> d."</string>
+    <string name="duration_years_medium_past" msgid="893797065424596243">"fyrir <xliff:g id="COUNT">%d</xliff:g> árum"</string>
     <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{Fyrir # mínútu}one{Fyrir # mínútu}other{Fyrir # mínútum}}"</string>
     <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{Fyrir # klukkustund}one{Fyrir # klukkustund}other{Fyrir # klukkustundum}}"</string>
     <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{Fyrir # degi}one{Fyrir # degi}other{Fyrir # dögum}}"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index e912417..9fd7913 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -1159,38 +1159,22 @@
     <string name="duration_hours_shortest_future" msgid="2979276794547981674">"tra <xliff:g id="COUNT">%d</xliff:g> h"</string>
     <string name="duration_days_shortest_future" msgid="3392722163935571543">"tra <xliff:g id="COUNT">%d</xliff:g> g"</string>
     <string name="duration_years_shortest_future" msgid="5537464088352970388">"tra <xliff:g id="COUNT">%d</xliff:g> a"</string>
-    <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
-    <skip />
-    <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
-    <skip />
-    <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
-    <skip />
-    <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium (1465359726485910115) -->
-    <skip />
-    <!-- no translation found for duration_days_medium (5994225628248661388) -->
-    <skip />
-    <!-- no translation found for duration_years_medium (734023884353592526) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_past (893797065424596243) -->
-    <skip />
+    <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g> min fa"</string>
+    <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g> h fa"</string>
+    <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g> g fa"</string>
+    <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g> anni fa"</string>
+    <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> min"</string>
+    <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> h"</string>
+    <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> g"</string>
+    <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> anno"</string>
+    <string name="duration_minutes_medium_future" msgid="2750894988731934402">"tra <xliff:g id="COUNT">%d</xliff:g> min"</string>
+    <string name="duration_hours_medium_future" msgid="6050833881463849764">"tra <xliff:g id="COUNT">%d</xliff:g> h"</string>
+    <string name="duration_days_medium_future" msgid="1700821545602729963">"tra <xliff:g id="COUNT">%d</xliff:g> g"</string>
+    <string name="duration_years_medium_future" msgid="3281018940397120166">"tra <xliff:g id="COUNT">%d</xliff:g> anno"</string>
+    <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g> min fa"</string>
+    <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g> h fa"</string>
+    <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g> g fa"</string>
+    <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g> anni fa"</string>
     <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# minuto fa}many{# di minuti fa}other{# minuti fa}}"</string>
     <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# ora fa}many{# di ore fa}other{# ore fa}}"</string>
     <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# giorno fa}many{# di giorni fa}other{# giorni fa}}"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index a9ce2d2..3a5c63c 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -1159,38 +1159,22 @@
     <string name="duration_hours_shortest_future" msgid="2979276794547981674">"בעוד <xliff:g id="COUNT">%d</xliff:g> שע‘"</string>
     <string name="duration_days_shortest_future" msgid="3392722163935571543">"בעוד <xliff:g id="COUNT">%d</xliff:g> י‘"</string>
     <string name="duration_years_shortest_future" msgid="5537464088352970388">"בעוד <xliff:g id="COUNT">%d</xliff:g> שנים"</string>
-    <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
-    <skip />
-    <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
-    <skip />
-    <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
-    <skip />
-    <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium (1465359726485910115) -->
-    <skip />
-    <!-- no translation found for duration_days_medium (5994225628248661388) -->
-    <skip />
-    <!-- no translation found for duration_years_medium (734023884353592526) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_past (893797065424596243) -->
-    <skip />
+    <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"לפני <xliff:g id="COUNT">%d</xliff:g>דק\'"</string>
+    <string name="duration_hours_shortest_past" msgid="2098397414186628489">"לפני <xliff:g id="COUNT">%d</xliff:g>שע\'"</string>
+    <string name="duration_days_shortest_past" msgid="1832006037955897625">"לפני <xliff:g id="COUNT">%d</xliff:g>י\'"</string>
+    <string name="duration_years_shortest_past" msgid="6168256514200469291">"לפני <xliff:g id="COUNT">%d</xliff:g>שנ\'"</string>
+    <string name="duration_minutes_medium" msgid="5891933490342643944">"‫<xliff:g id="COUNT">%d</xliff:g> דקות"</string>
+    <string name="duration_hours_medium" msgid="1465359726485910115">"‫<xliff:g id="COUNT">%d</xliff:g> שעות"</string>
+    <string name="duration_days_medium" msgid="5994225628248661388">"‫<xliff:g id="COUNT">%d</xliff:g> ימים"</string>
+    <string name="duration_years_medium" msgid="734023884353592526">"‫<xliff:g id="COUNT">%d</xliff:g> שנים"</string>
+    <string name="duration_minutes_medium_future" msgid="2750894988731934402">"בעוד <xliff:g id="COUNT">%d</xliff:g> דקות"</string>
+    <string name="duration_hours_medium_future" msgid="6050833881463849764">"בעוד <xliff:g id="COUNT">%d</xliff:g> שעות"</string>
+    <string name="duration_days_medium_future" msgid="1700821545602729963">"בעוד <xliff:g id="COUNT">%d</xliff:g> ימים"</string>
+    <string name="duration_years_medium_future" msgid="3281018940397120166">"בעוד <xliff:g id="COUNT">%d</xliff:g> שנים"</string>
+    <string name="duration_minutes_medium_past" msgid="7400424340181947714">"לפני <xliff:g id="COUNT">%d</xliff:g> דקות"</string>
+    <string name="duration_hours_medium_past" msgid="6709441336035202617">"לפני <xliff:g id="COUNT">%d</xliff:g> שעות"</string>
+    <string name="duration_days_medium_past" msgid="5748156261134344532">"לפני <xliff:g id="COUNT">%d</xliff:g> ימים"</string>
+    <string name="duration_years_medium_past" msgid="893797065424596243">"לפני <xliff:g id="COUNT">%d</xliff:g> שנים"</string>
     <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{לפני דקה}one{לפני # דקות}two{לפני # דקות}other{לפני # דקות}}"</string>
     <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{לפני שעה}one{לפני # שעות}two{לפני שעתיים}other{לפני # שעות}}"</string>
     <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{לפני יום}one{לפני # ימים}two{לפני יומיים}other{לפני # ימים}}"</string>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index b52896b..10cb0c3 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -1158,38 +1158,22 @@
     <string name="duration_hours_shortest_future" msgid="2979276794547981674">"<xliff:g id="COUNT">%d</xliff:g> საათში"</string>
     <string name="duration_days_shortest_future" msgid="3392722163935571543">"<xliff:g id="COUNT">%d</xliff:g> დღეში"</string>
     <string name="duration_years_shortest_future" msgid="5537464088352970388">"<xliff:g id="COUNT">%d</xliff:g> წელში"</string>
-    <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
-    <skip />
-    <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
-    <skip />
-    <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
-    <skip />
-    <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium (1465359726485910115) -->
-    <skip />
-    <!-- no translation found for duration_days_medium (5994225628248661388) -->
-    <skip />
-    <!-- no translation found for duration_years_medium (734023884353592526) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_past (893797065424596243) -->
-    <skip />
+    <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g> წთ.-ის წინ"</string>
+    <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g> სთ.-ის წინ"</string>
+    <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g> დღის წინ"</string>
+    <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g> წლის წინ"</string>
+    <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> წთ."</string>
+    <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> სთ."</string>
+    <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> დღე"</string>
+    <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> წელი"</string>
+    <string name="duration_minutes_medium_future" msgid="2750894988731934402">"<xliff:g id="COUNT">%d</xliff:g> წთ.-ში"</string>
+    <string name="duration_hours_medium_future" msgid="6050833881463849764">"<xliff:g id="COUNT">%d</xliff:g> სთ.-ში"</string>
+    <string name="duration_days_medium_future" msgid="1700821545602729963">"<xliff:g id="COUNT">%d</xliff:g> დღეში"</string>
+    <string name="duration_years_medium_future" msgid="3281018940397120166">"<xliff:g id="COUNT">%d</xliff:g> წელში"</string>
+    <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g> წთ.-ის წინ"</string>
+    <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g> სთ.-ის წინ"</string>
+    <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g> დღის წინ"</string>
+    <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g> წლის წინ"</string>
     <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# წუთის წინ}other{# წუთის წინ}}"</string>
     <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# საათის წინ}other{# საათის წინ}}"</string>
     <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# დღის წინ}other{# დღის წინ}}"</string>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index 27299c5..2db0fcd 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -1158,38 +1158,22 @@
     <string name="duration_hours_shortest_future" msgid="2979276794547981674">"ក្នុងរយៈពេល <xliff:g id="COUNT">%d</xliff:g>ម៉"</string>
     <string name="duration_days_shortest_future" msgid="3392722163935571543">"ក្នុងរយៈពេល <xliff:g id="COUNT">%d</xliff:g>ថ"</string>
     <string name="duration_years_shortest_future" msgid="5537464088352970388">"ក្នុងរយៈពេល <xliff:g id="COUNT">%d</xliff:g>ឆ"</string>
-    <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
-    <skip />
-    <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
-    <skip />
-    <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
-    <skip />
-    <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium (1465359726485910115) -->
-    <skip />
-    <!-- no translation found for duration_days_medium (5994225628248661388) -->
-    <skip />
-    <!-- no translation found for duration_years_medium (734023884353592526) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_past (893797065424596243) -->
-    <skip />
+    <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g> នាទីមុន"</string>
+    <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g> ម៉ោងមុន"</string>
+    <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g> ថ្ងៃមុន"</string>
+    <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g> ឆ្នាំមុន"</string>
+    <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> ន"</string>
+    <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> ម៉"</string>
+    <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> ថ្ងៃ"</string>
+    <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> ឆ្នាំ"</string>
+    <string name="duration_minutes_medium_future" msgid="2750894988731934402">"ក្នុងរយៈពេល <xliff:g id="COUNT">%d</xliff:g> នាទី"</string>
+    <string name="duration_hours_medium_future" msgid="6050833881463849764">"ក្នុងរយៈពេល <xliff:g id="COUNT">%d</xliff:g> ម៉ោង"</string>
+    <string name="duration_days_medium_future" msgid="1700821545602729963">"ក្នុងរយៈពេល <xliff:g id="COUNT">%d</xliff:g> ថ្ងៃ"</string>
+    <string name="duration_years_medium_future" msgid="3281018940397120166">"ក្នុងរយៈពេល <xliff:g id="COUNT">%d</xliff:g> ឆ្នាំ"</string>
+    <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g> នាទីមុន"</string>
+    <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g> ម៉ោងមុន"</string>
+    <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g> ថ្ងៃមុន"</string>
+    <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g> ឆ្នាំមុន"</string>
     <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# នាទី​មុន}other{# នាទីមុន}}"</string>
     <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# ម៉ោងមុន}other{# ម៉ោងមុន}}"</string>
     <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# ថ្ងៃមុន}other{# ថ្ងៃមុន}}"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 5a476836..38e5bb5 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -1158,38 +1158,22 @@
     <string name="duration_hours_shortest_future" msgid="2979276794547981674">"<xliff:g id="COUNT">%d</xliff:g>시간 후"</string>
     <string name="duration_days_shortest_future" msgid="3392722163935571543">"<xliff:g id="COUNT">%d</xliff:g>일 후"</string>
     <string name="duration_years_shortest_future" msgid="5537464088352970388">"<xliff:g id="COUNT">%d</xliff:g>년 후"</string>
-    <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
-    <skip />
-    <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
-    <skip />
-    <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
-    <skip />
-    <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium (1465359726485910115) -->
-    <skip />
-    <!-- no translation found for duration_days_medium (5994225628248661388) -->
-    <skip />
-    <!-- no translation found for duration_years_medium (734023884353592526) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_past (893797065424596243) -->
-    <skip />
+    <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g>분 전"</string>
+    <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g>시간 전"</string>
+    <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g>일 전"</string>
+    <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g>년 전"</string>
+    <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g>분"</string>
+    <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g>시간"</string>
+    <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g>일"</string>
+    <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g>년"</string>
+    <string name="duration_minutes_medium_future" msgid="2750894988731934402">"<xliff:g id="COUNT">%d</xliff:g>분 후"</string>
+    <string name="duration_hours_medium_future" msgid="6050833881463849764">"<xliff:g id="COUNT">%d</xliff:g>시간 후"</string>
+    <string name="duration_days_medium_future" msgid="1700821545602729963">"<xliff:g id="COUNT">%d</xliff:g>일 후"</string>
+    <string name="duration_years_medium_future" msgid="3281018940397120166">"<xliff:g id="COUNT">%d</xliff:g>년 후"</string>
+    <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g>분 전"</string>
+    <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g>시간 전"</string>
+    <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g>일 전"</string>
+    <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g>년 전"</string>
     <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{#분 전}other{#분 전}}"</string>
     <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{#시간 전}other{#시간 전}}"</string>
     <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{#일 전}other{#일 전}}"</string>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index 2ca59918..60e2749 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -1158,38 +1158,22 @@
     <string name="duration_hours_shortest_future" msgid="2979276794547981674">"<xliff:g id="COUNT">%d</xliff:g> с. кийин"</string>
     <string name="duration_days_shortest_future" msgid="3392722163935571543">"<xliff:g id="COUNT">%d</xliff:g> к. кийин"</string>
     <string name="duration_years_shortest_future" msgid="5537464088352970388">"<xliff:g id="COUNT">%d</xliff:g> ж. кийин"</string>
-    <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
-    <skip />
-    <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
-    <skip />
-    <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
-    <skip />
-    <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium (1465359726485910115) -->
-    <skip />
-    <!-- no translation found for duration_days_medium (5994225628248661388) -->
-    <skip />
-    <!-- no translation found for duration_years_medium (734023884353592526) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_past (893797065424596243) -->
-    <skip />
+    <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g> мүн. мурун"</string>
+    <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g> с. мурун"</string>
+    <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g> к. мурун"</string>
+    <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g> ж. мурун"</string>
+    <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> мүн."</string>
+    <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> с."</string>
+    <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> к."</string>
+    <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> ж."</string>
+    <string name="duration_minutes_medium_future" msgid="2750894988731934402">"<xliff:g id="COUNT">%d</xliff:g> мүн. кийин"</string>
+    <string name="duration_hours_medium_future" msgid="6050833881463849764">"<xliff:g id="COUNT">%d</xliff:g> с. кийин"</string>
+    <string name="duration_days_medium_future" msgid="1700821545602729963">"<xliff:g id="COUNT">%d</xliff:g> к. кийин"</string>
+    <string name="duration_years_medium_future" msgid="3281018940397120166">"<xliff:g id="COUNT">%d</xliff:g> ж. кийин"</string>
+    <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g> мүн. мурун"</string>
+    <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g> с. мурун"</string>
+    <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g> к. мурун"</string>
+    <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g> ж. мурун"</string>
     <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# мүнөт мурун}other{# мүнөт мурун}}"</string>
     <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# саат мурун}other{# саат мурун}}"</string>
     <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# күн мурун}other{# күн мурун}}"</string>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index 3b234e8..da2d1ad 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -1158,38 +1158,22 @@
     <string name="duration_hours_shortest_future" msgid="2979276794547981674">"ໃນ <xliff:g id="COUNT">%d</xliff:g>ຊມ"</string>
     <string name="duration_days_shortest_future" msgid="3392722163935571543">"ໃນ <xliff:g id="COUNT">%d</xliff:g>ມ"</string>
     <string name="duration_years_shortest_future" msgid="5537464088352970388">"ໃນ <xliff:g id="COUNT">%d</xliff:g>ປ"</string>
-    <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
-    <skip />
-    <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
-    <skip />
-    <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
-    <skip />
-    <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium (1465359726485910115) -->
-    <skip />
-    <!-- no translation found for duration_days_medium (5994225628248661388) -->
-    <skip />
-    <!-- no translation found for duration_years_medium (734023884353592526) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_past (893797065424596243) -->
-    <skip />
+    <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g> ນາທີກ່ອນ"</string>
+    <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g> ຊົ່ວໂມງກ່ອນ"</string>
+    <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g> ມື້ກ່ອນ"</string>
+    <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g> ປີກ່ອນ"</string>
+    <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> ນາທີ"</string>
+    <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> ຊົ່ວໂມງ"</string>
+    <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> ມື້"</string>
+    <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> ປີ"</string>
+    <string name="duration_minutes_medium_future" msgid="2750894988731934402">"ໃນ <xliff:g id="COUNT">%d</xliff:g> ນາທີ"</string>
+    <string name="duration_hours_medium_future" msgid="6050833881463849764">"ໃນ <xliff:g id="COUNT">%d</xliff:g> ຊົ່ວໂມງ"</string>
+    <string name="duration_days_medium_future" msgid="1700821545602729963">"ໃນ <xliff:g id="COUNT">%d</xliff:g> ມື້"</string>
+    <string name="duration_years_medium_future" msgid="3281018940397120166">"ໃນ <xliff:g id="COUNT">%d</xliff:g> ປີ"</string>
+    <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g> ນາທີກ່ອນ"</string>
+    <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g> ຊົ່ວໂມງກ່ອນ"</string>
+    <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g> ມື້ກ່ອນ"</string>
+    <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g> ປີກ່ອນ"</string>
     <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# ນາທີກ່ອນ}other{# ນາທີກ່ອນ}}"</string>
     <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# ຊົ່ວໂມງກ່ອນ}other{# ຊົ່ວໂມງກ່ອນ}}"</string>
     <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# ມື້ກ່ອນ}other{# ມື້ກ່ອນ}}"</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index c1eaa04..5de9881 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -1160,38 +1160,22 @@
     <string name="duration_hours_shortest_future" msgid="2979276794547981674">"po <xliff:g id="COUNT">%d</xliff:g> val."</string>
     <string name="duration_days_shortest_future" msgid="3392722163935571543">"po <xliff:g id="COUNT">%d</xliff:g> d."</string>
     <string name="duration_years_shortest_future" msgid="5537464088352970388">"po <xliff:g id="COUNT">%d</xliff:g> m."</string>
-    <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
-    <skip />
-    <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
-    <skip />
-    <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
-    <skip />
-    <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium (1465359726485910115) -->
-    <skip />
-    <!-- no translation found for duration_days_medium (5994225628248661388) -->
-    <skip />
-    <!-- no translation found for duration_years_medium (734023884353592526) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_past (893797065424596243) -->
-    <skip />
+    <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"prieš <xliff:g id="COUNT">%d</xliff:g> min."</string>
+    <string name="duration_hours_shortest_past" msgid="2098397414186628489">"prieš <xliff:g id="COUNT">%d</xliff:g> val."</string>
+    <string name="duration_days_shortest_past" msgid="1832006037955897625">"prieš <xliff:g id="COUNT">%d</xliff:g> d."</string>
+    <string name="duration_years_shortest_past" msgid="6168256514200469291">"prieš <xliff:g id="COUNT">%d</xliff:g> m."</string>
+    <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> min."</string>
+    <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> val."</string>
+    <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> d."</string>
+    <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> m."</string>
+    <string name="duration_minutes_medium_future" msgid="2750894988731934402">"po <xliff:g id="COUNT">%d</xliff:g> min."</string>
+    <string name="duration_hours_medium_future" msgid="6050833881463849764">"po <xliff:g id="COUNT">%d</xliff:g> val."</string>
+    <string name="duration_days_medium_future" msgid="1700821545602729963">"po <xliff:g id="COUNT">%d</xliff:g> d."</string>
+    <string name="duration_years_medium_future" msgid="3281018940397120166">"po <xliff:g id="COUNT">%d</xliff:g> m."</string>
+    <string name="duration_minutes_medium_past" msgid="7400424340181947714">"prieš <xliff:g id="COUNT">%d</xliff:g> min."</string>
+    <string name="duration_hours_medium_past" msgid="6709441336035202617">"prieš <xliff:g id="COUNT">%d</xliff:g> val."</string>
+    <string name="duration_days_medium_past" msgid="5748156261134344532">"prieš <xliff:g id="COUNT">%d</xliff:g> d."</string>
+    <string name="duration_years_medium_past" msgid="893797065424596243">"prieš <xliff:g id="COUNT">%d</xliff:g> m."</string>
     <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{Prieš # minutę}one{Prieš # minutę}few{Prieš # minutes}many{Prieš # minutės}other{Prieš # minučių}}"</string>
     <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{Prieš # valandą}one{Prieš # valandą}few{Prieš # valandas}many{Prieš # valandos}other{Prieš # valandų}}"</string>
     <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{Prieš # dieną}one{Prieš # dieną}few{Prieš # dienas}many{Prieš # dienos}other{Prieš # dienų}}"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 03c7c3c..ac109a8 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -1159,38 +1159,22 @@
     <string name="duration_hours_shortest_future" msgid="2979276794547981674">"pēc <xliff:g id="COUNT">%d</xliff:g> h"</string>
     <string name="duration_days_shortest_future" msgid="3392722163935571543">"pēc <xliff:g id="COUNT">%d</xliff:g> d."</string>
     <string name="duration_years_shortest_future" msgid="5537464088352970388">"pēc <xliff:g id="COUNT">%d</xliff:g> g."</string>
-    <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
-    <skip />
-    <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
-    <skip />
-    <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
-    <skip />
-    <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium (1465359726485910115) -->
-    <skip />
-    <!-- no translation found for duration_days_medium (5994225628248661388) -->
-    <skip />
-    <!-- no translation found for duration_years_medium (734023884353592526) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_past (893797065424596243) -->
-    <skip />
+    <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"pirms <xliff:g id="COUNT">%d</xliff:g> min"</string>
+    <string name="duration_hours_shortest_past" msgid="2098397414186628489">"pirms <xliff:g id="COUNT">%d</xliff:g> h"</string>
+    <string name="duration_days_shortest_past" msgid="1832006037955897625">"pirms <xliff:g id="COUNT">%d</xliff:g> d."</string>
+    <string name="duration_years_shortest_past" msgid="6168256514200469291">"pirms <xliff:g id="COUNT">%d</xliff:g> g."</string>
+    <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> min"</string>
+    <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> h"</string>
+    <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> d."</string>
+    <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> g."</string>
+    <string name="duration_minutes_medium_future" msgid="2750894988731934402">"pēc <xliff:g id="COUNT">%d</xliff:g> min"</string>
+    <string name="duration_hours_medium_future" msgid="6050833881463849764">"pēc <xliff:g id="COUNT">%d</xliff:g> h"</string>
+    <string name="duration_days_medium_future" msgid="1700821545602729963">"pēc <xliff:g id="COUNT">%d</xliff:g> d."</string>
+    <string name="duration_years_medium_future" msgid="3281018940397120166">"pēc <xliff:g id="COUNT">%d</xliff:g> g."</string>
+    <string name="duration_minutes_medium_past" msgid="7400424340181947714">"pirms <xliff:g id="COUNT">%d</xliff:g> min"</string>
+    <string name="duration_hours_medium_past" msgid="6709441336035202617">"pirms <xliff:g id="COUNT">%d</xliff:g> h"</string>
+    <string name="duration_days_medium_past" msgid="5748156261134344532">"pirms <xliff:g id="COUNT">%d</xliff:g> d."</string>
+    <string name="duration_years_medium_past" msgid="893797065424596243">"pirms <xliff:g id="COUNT">%d</xliff:g> g."</string>
     <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{Pirms vienas minūtes}zero{Pirms # minūtēm}one{Pirms vairākām minūtēm, minūšu skaits: #}other{Pirms vairākām minūtēm, minūšu skaits: #}}"</string>
     <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{Pirms vienas stundas}zero{Pirms # stundām}one{Pirms vairākām stundām, stundu skaits: #}other{Pirms vairākām stundām, stundu skaits: #}}"</string>
     <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{Pirms vienas dienas}zero{Pirms # dienām}one{Pirms vairākām dienām, dienu skaits: #}other{Pirms vairākām dienām, dienu skaits: #}}"</string>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index e299c8b..1f30da3 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -1158,38 +1158,22 @@
     <string name="duration_hours_shortest_future" msgid="2979276794547981674">"по <xliff:g id="COUNT">%d</xliff:g> ч."</string>
     <string name="duration_days_shortest_future" msgid="3392722163935571543">"по <xliff:g id="COUNT">%d</xliff:g> д."</string>
     <string name="duration_years_shortest_future" msgid="5537464088352970388">"по <xliff:g id="COUNT">%d</xliff:g> г."</string>
-    <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
-    <skip />
-    <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
-    <skip />
-    <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
-    <skip />
-    <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium (1465359726485910115) -->
-    <skip />
-    <!-- no translation found for duration_days_medium (5994225628248661388) -->
-    <skip />
-    <!-- no translation found for duration_years_medium (734023884353592526) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_past (893797065424596243) -->
-    <skip />
+    <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"пред <xliff:g id="COUNT">%d</xliff:g> мин."</string>
+    <string name="duration_hours_shortest_past" msgid="2098397414186628489">"пред <xliff:g id="COUNT">%d</xliff:g> ч."</string>
+    <string name="duration_days_shortest_past" msgid="1832006037955897625">"пред <xliff:g id="COUNT">%d</xliff:g> д."</string>
+    <string name="duration_years_shortest_past" msgid="6168256514200469291">"пред <xliff:g id="COUNT">%d</xliff:g> год."</string>
+    <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> мин."</string>
+    <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> ч."</string>
+    <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> д."</string>
+    <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> год."</string>
+    <string name="duration_minutes_medium_future" msgid="2750894988731934402">"по <xliff:g id="COUNT">%d</xliff:g> мин."</string>
+    <string name="duration_hours_medium_future" msgid="6050833881463849764">"по <xliff:g id="COUNT">%d</xliff:g> ч."</string>
+    <string name="duration_days_medium_future" msgid="1700821545602729963">"по <xliff:g id="COUNT">%d</xliff:g> д."</string>
+    <string name="duration_years_medium_future" msgid="3281018940397120166">"по <xliff:g id="COUNT">%d</xliff:g> год."</string>
+    <string name="duration_minutes_medium_past" msgid="7400424340181947714">"пред <xliff:g id="COUNT">%d</xliff:g> мин."</string>
+    <string name="duration_hours_medium_past" msgid="6709441336035202617">"пред <xliff:g id="COUNT">%d</xliff:g> ч."</string>
+    <string name="duration_days_medium_past" msgid="5748156261134344532">"пред <xliff:g id="COUNT">%d</xliff:g> д."</string>
+    <string name="duration_years_medium_past" msgid="893797065424596243">"пред <xliff:g id="COUNT">%d</xliff:g> год."</string>
     <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{Пред # минута}one{Пред # минута}other{Пред # минути}}"</string>
     <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{Пред # час}one{Пред # час}other{Пред # часа}}"</string>
     <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{Пред # ден}one{Пред # ден}other{Пред # дена}}"</string>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index 3c08c01..7f8027d 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -1158,38 +1158,22 @@
     <string name="duration_hours_shortest_future" msgid="2979276794547981674">"<xliff:g id="COUNT">%d</xliff:g>മണിക്കൂറിൽ"</string>
     <string name="duration_days_shortest_future" msgid="3392722163935571543">"<xliff:g id="COUNT">%d</xliff:g>ദിവസത്തിൽ"</string>
     <string name="duration_years_shortest_future" msgid="5537464088352970388">"<xliff:g id="COUNT">%d</xliff:g>വർഷത്തിനുള്ളിൽ"</string>
-    <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
-    <skip />
-    <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
-    <skip />
-    <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
-    <skip />
-    <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium (1465359726485910115) -->
-    <skip />
-    <!-- no translation found for duration_days_medium (5994225628248661388) -->
-    <skip />
-    <!-- no translation found for duration_years_medium (734023884353592526) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_past (893797065424596243) -->
-    <skip />
+    <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g>m മുമ്പ്"</string>
+    <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g>h മുമ്പ്"</string>
+    <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g>ദിവസം മുമ്പ്"</string>
+    <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g>വർഷം മുമ്പ്"</string>
+    <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g>മിനിറ്റ്"</string>
+    <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g>മണിക്കൂർ"</string>
+    <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g>ദിവസം"</string>
+    <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g>വർഷം"</string>
+    <string name="duration_minutes_medium_future" msgid="2750894988731934402">"<xliff:g id="COUNT">%d</xliff:g>മിനിറ്റിൽ"</string>
+    <string name="duration_hours_medium_future" msgid="6050833881463849764">"<xliff:g id="COUNT">%d</xliff:g>മണിക്കൂറിൽ"</string>
+    <string name="duration_days_medium_future" msgid="1700821545602729963">"<xliff:g id="COUNT">%d</xliff:g>ദിവസത്തിനുള്ളിൽ"</string>
+    <string name="duration_years_medium_future" msgid="3281018940397120166">"<xliff:g id="COUNT">%d</xliff:g>വർഷത്തിൽ"</string>
+    <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g>മിനിറ്റ് മുമ്പ്"</string>
+    <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g>മണിക്കൂർ മുമ്പ്"</string>
+    <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g>ദിവസം മുമ്പ്"</string>
+    <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g>വർഷം മുമ്പ്"</string>
     <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# മിനിറ്റ് മുമ്പ്}other{# മിനിറ്റ് മുമ്പ്}}"</string>
     <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# മണിക്കൂർ മുമ്പ്}other{# മണിക്കൂർ മുമ്പ്}}"</string>
     <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# ദിവസം മുമ്പ്}other{# ദിവസം മുമ്പ്}}"</string>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index e32eff0..8043bae 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -1158,38 +1158,22 @@
     <string name="duration_hours_shortest_future" msgid="2979276794547981674">"<xliff:g id="COUNT">%d</xliff:g>цагийн дараа"</string>
     <string name="duration_days_shortest_future" msgid="3392722163935571543">"<xliff:g id="COUNT">%d</xliff:g>хоногийн дараа"</string>
     <string name="duration_years_shortest_future" msgid="5537464088352970388">"<xliff:g id="COUNT">%d</xliff:g>жилийн дараа"</string>
-    <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
-    <skip />
-    <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
-    <skip />
-    <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
-    <skip />
-    <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium (1465359726485910115) -->
-    <skip />
-    <!-- no translation found for duration_days_medium (5994225628248661388) -->
-    <skip />
-    <!-- no translation found for duration_years_medium (734023884353592526) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_past (893797065424596243) -->
-    <skip />
+    <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g> минутын өмнө"</string>
+    <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g> цагийн өмнө"</string>
+    <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g> хоногийн өмнө"</string>
+    <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g> жилийн өмнө"</string>
+    <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> минут"</string>
+    <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> цаг"</string>
+    <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> хоног"</string>
+    <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> жил"</string>
+    <string name="duration_minutes_medium_future" msgid="2750894988731934402">"<xliff:g id="COUNT">%d</xliff:g> минутын дараа"</string>
+    <string name="duration_hours_medium_future" msgid="6050833881463849764">"<xliff:g id="COUNT">%d</xliff:g> цагийн дараа"</string>
+    <string name="duration_days_medium_future" msgid="1700821545602729963">"<xliff:g id="COUNT">%d</xliff:g> хоногийн дараа"</string>
+    <string name="duration_years_medium_future" msgid="3281018940397120166">"<xliff:g id="COUNT">%d</xliff:g> жилийн дараа"</string>
+    <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g> минутын өмнө"</string>
+    <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g> цагийн өмнө"</string>
+    <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g> хоногийн өмнө"</string>
+    <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g> жилийн өмнө"</string>
     <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# минутын өмнө}other{# минутын өмнө}}"</string>
     <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# цагийн өмнө}other{# цагийн өмнө}}"</string>
     <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# хоногийн өмнө}other{# хоногийн өмнө}}"</string>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index abe9a94..7bdd942 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -1158,38 +1158,22 @@
     <string name="duration_hours_shortest_future" msgid="2979276794547981674">"<xliff:g id="COUNT">%d</xliff:g> तासांमध्ये"</string>
     <string name="duration_days_shortest_future" msgid="3392722163935571543">"<xliff:g id="COUNT">%d</xliff:g> दिवसांमध्ये"</string>
     <string name="duration_years_shortest_future" msgid="5537464088352970388">"<xliff:g id="COUNT">%d</xliff:g> वर्षांमध्ये"</string>
-    <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
-    <skip />
-    <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
-    <skip />
-    <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
-    <skip />
-    <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium (1465359726485910115) -->
-    <skip />
-    <!-- no translation found for duration_days_medium (5994225628248661388) -->
-    <skip />
-    <!-- no translation found for duration_years_medium (734023884353592526) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_past (893797065424596243) -->
-    <skip />
+    <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g>मिनिटापूर्वी"</string>
+    <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g>तासापूर्वी"</string>
+    <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g>दिवसापूर्वी"</string>
+    <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g>वर्षापूर्वी"</string>
+    <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g>मिनिट"</string>
+    <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g>तास"</string>
+    <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g>दिवस"</string>
+    <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g>वर्ष"</string>
+    <string name="duration_minutes_medium_future" msgid="2750894988731934402">"<xliff:g id="COUNT">%d</xliff:g>मिनिटामध्‍ये"</string>
+    <string name="duration_hours_medium_future" msgid="6050833881463849764">"<xliff:g id="COUNT">%d</xliff:g>तासामध्ये"</string>
+    <string name="duration_days_medium_future" msgid="1700821545602729963">"<xliff:g id="COUNT">%d</xliff:g>दिवसामध्ये"</string>
+    <string name="duration_years_medium_future" msgid="3281018940397120166">"<xliff:g id="COUNT">%d</xliff:g>वर्षामध्ये"</string>
+    <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g>मिनिटापूर्वी"</string>
+    <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g>तासापूर्वी"</string>
+    <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g>दिवसापूर्वी"</string>
+    <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g>वर्षापूर्वी"</string>
     <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# मिनिटापूर्वी}other{# मिनिटांपूर्वी}}"</string>
     <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# तासापूर्वी}other{# तासांपूर्वी}}"</string>
     <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# दिवसापूर्वी}other{# दिवसांपूर्वी}}"</string>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index ec6eff4..61af771 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -1158,38 +1158,22 @@
     <string name="duration_hours_shortest_future" msgid="2979276794547981674">"<xliff:g id="COUNT">%d</xliff:g> နာရီအတွင်း"</string>
     <string name="duration_days_shortest_future" msgid="3392722163935571543">"<xliff:g id="COUNT">%d</xliff:g> ရက်အတွင်း"</string>
     <string name="duration_years_shortest_future" msgid="5537464088352970388">"<xliff:g id="COUNT">%d</xliff:g> နှစ်အတွင်း"</string>
-    <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
-    <skip />
-    <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
-    <skip />
-    <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
-    <skip />
-    <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium (1465359726485910115) -->
-    <skip />
-    <!-- no translation found for duration_days_medium (5994225628248661388) -->
-    <skip />
-    <!-- no translation found for duration_years_medium (734023884353592526) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_past (893797065424596243) -->
-    <skip />
+    <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"ပြီးခဲ့သော <xliff:g id="COUNT">%d</xliff:g> မိနစ်"</string>
+    <string name="duration_hours_shortest_past" msgid="2098397414186628489">"ပြီးခဲ့သော <xliff:g id="COUNT">%d</xliff:g> နာရီ"</string>
+    <string name="duration_days_shortest_past" msgid="1832006037955897625">"ပြီးခဲ့သော <xliff:g id="COUNT">%d</xliff:g> ရက်"</string>
+    <string name="duration_years_shortest_past" msgid="6168256514200469291">"ပြီးခဲ့သော <xliff:g id="COUNT">%d</xliff:g> နှစ်"</string>
+    <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> မိနစ်"</string>
+    <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> နာရီ"</string>
+    <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> ရက်"</string>
+    <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> နှစ်"</string>
+    <string name="duration_minutes_medium_future" msgid="2750894988731934402">"<xliff:g id="COUNT">%d</xliff:g> မိနစ်အတွင်း"</string>
+    <string name="duration_hours_medium_future" msgid="6050833881463849764">"<xliff:g id="COUNT">%d</xliff:g> နာရီအတွင်း"</string>
+    <string name="duration_days_medium_future" msgid="1700821545602729963">"<xliff:g id="COUNT">%d</xliff:g> ရက်အတွင်း"</string>
+    <string name="duration_years_medium_future" msgid="3281018940397120166">"<xliff:g id="COUNT">%d</xliff:g> နှစ်အတွင်း"</string>
+    <string name="duration_minutes_medium_past" msgid="7400424340181947714">"ပြီးခဲ့သော <xliff:g id="COUNT">%d</xliff:g> မိနစ်"</string>
+    <string name="duration_hours_medium_past" msgid="6709441336035202617">"ပြီးခဲ့သော <xliff:g id="COUNT">%d</xliff:g> နာရီ"</string>
+    <string name="duration_days_medium_past" msgid="5748156261134344532">"ပြီးခဲ့သော <xliff:g id="COUNT">%d</xliff:g> ရက်"</string>
+    <string name="duration_years_medium_past" msgid="893797065424596243">"ပြီးခဲ့သော <xliff:g id="COUNT">%d</xliff:g> နှစ်"</string>
     <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{ပြီးခဲ့သော # မိနစ်}other{ပြီးခဲ့သော # မိနစ်}}"</string>
     <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{ပြီးခဲ့သော # နာရီ}other{ပြီးခဲ့သော # နာရီ}}"</string>
     <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{ပြီးခဲ့သော # ရက်}other{ပြီးခဲ့သော # ရက်}}"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index fd80ab4..0ddbc41 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -1158,38 +1158,22 @@
     <string name="duration_hours_shortest_future" msgid="2979276794547981674">"om <xliff:g id="COUNT">%d</xliff:g> t"</string>
     <string name="duration_days_shortest_future" msgid="3392722163935571543">"om <xliff:g id="COUNT">%d</xliff:g> d"</string>
     <string name="duration_years_shortest_future" msgid="5537464088352970388">"om <xliff:g id="COUNT">%d</xliff:g> år"</string>
-    <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
-    <skip />
-    <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
-    <skip />
-    <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
-    <skip />
-    <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium (1465359726485910115) -->
-    <skip />
-    <!-- no translation found for duration_days_medium (5994225628248661388) -->
-    <skip />
-    <!-- no translation found for duration_years_medium (734023884353592526) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_past (893797065424596243) -->
-    <skip />
+    <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"for <xliff:g id="COUNT">%d</xliff:g>m siden"</string>
+    <string name="duration_hours_shortest_past" msgid="2098397414186628489">"for <xliff:g id="COUNT">%d</xliff:g>t siden"</string>
+    <string name="duration_days_shortest_past" msgid="1832006037955897625">"for <xliff:g id="COUNT">%d</xliff:g>d siden"</string>
+    <string name="duration_years_shortest_past" msgid="6168256514200469291">"for <xliff:g id="COUNT">%d</xliff:g> år siden"</string>
+    <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> min"</string>
+    <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g>t"</string>
+    <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g>d"</string>
+    <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> år"</string>
+    <string name="duration_minutes_medium_future" msgid="2750894988731934402">"om <xliff:g id="COUNT">%d</xliff:g> min"</string>
+    <string name="duration_hours_medium_future" msgid="6050833881463849764">"om <xliff:g id="COUNT">%d</xliff:g>t"</string>
+    <string name="duration_days_medium_future" msgid="1700821545602729963">"om <xliff:g id="COUNT">%d</xliff:g>d"</string>
+    <string name="duration_years_medium_future" msgid="3281018940397120166">"om <xliff:g id="COUNT">%d</xliff:g> år"</string>
+    <string name="duration_minutes_medium_past" msgid="7400424340181947714">"for <xliff:g id="COUNT">%d</xliff:g> min siden"</string>
+    <string name="duration_hours_medium_past" msgid="6709441336035202617">"for <xliff:g id="COUNT">%d</xliff:g>t siden"</string>
+    <string name="duration_days_medium_past" msgid="5748156261134344532">"for <xliff:g id="COUNT">%d</xliff:g>d siden"</string>
+    <string name="duration_years_medium_past" msgid="893797065424596243">"for <xliff:g id="COUNT">%d</xliff:g> år siden"</string>
     <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{for # minutt siden}other{For # minutter siden}}"</string>
     <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# time siden}other{# timer siden}}"</string>
     <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{For # dag siden}other{For # dager siden}}"</string>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index 6fabb06..c9a922e 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -1158,38 +1158,22 @@
     <string name="duration_hours_shortest_future" msgid="2979276794547981674">"<xliff:g id="COUNT">%d</xliff:g> घण्टाभित्र"</string>
     <string name="duration_days_shortest_future" msgid="3392722163935571543">"<xliff:g id="COUNT">%d</xliff:g> दिनभित्र"</string>
     <string name="duration_years_shortest_future" msgid="5537464088352970388">"<xliff:g id="COUNT">%d</xliff:g> वर्षभित्र"</string>
-    <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
-    <skip />
-    <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
-    <skip />
-    <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
-    <skip />
-    <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium (1465359726485910115) -->
-    <skip />
-    <!-- no translation found for duration_days_medium (5994225628248661388) -->
-    <skip />
-    <!-- no translation found for duration_years_medium (734023884353592526) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_past (893797065424596243) -->
-    <skip />
+    <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g> मिनेटअघि"</string>
+    <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g> घण्टाअघि"</string>
+    <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g> दिनअघि"</string>
+    <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g> वर्षअघि"</string>
+    <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> मिनेट"</string>
+    <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> घण्टा"</string>
+    <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> दिन"</string>
+    <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> वर्ष"</string>
+    <string name="duration_minutes_medium_future" msgid="2750894988731934402">"<xliff:g id="COUNT">%d</xliff:g> मिनेटपछि"</string>
+    <string name="duration_hours_medium_future" msgid="6050833881463849764">"<xliff:g id="COUNT">%d</xliff:g> घण्टापछि"</string>
+    <string name="duration_days_medium_future" msgid="1700821545602729963">"<xliff:g id="COUNT">%d</xliff:g> दिनपछि"</string>
+    <string name="duration_years_medium_future" msgid="3281018940397120166">"<xliff:g id="COUNT">%d</xliff:g> वर्षपछि"</string>
+    <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g> मिनेटअघि"</string>
+    <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g> घण्टाअघि"</string>
+    <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g> दिनअघि"</string>
+    <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g> वर्षअघि"</string>
     <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# मिनेटअघि}other{# मिनेटअघि}}"</string>
     <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# घण्टाअघि}other{# घण्टाअघि}}"</string>
     <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# दिनअघि}other{# दिनअघि}}"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index b93b916..e88384b 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -1158,38 +1158,22 @@
     <string name="duration_hours_shortest_future" msgid="2979276794547981674">"over <xliff:g id="COUNT">%d</xliff:g> u"</string>
     <string name="duration_days_shortest_future" msgid="3392722163935571543">"over <xliff:g id="COUNT">%d</xliff:g> d"</string>
     <string name="duration_years_shortest_future" msgid="5537464088352970388">"over <xliff:g id="COUNT">%d</xliff:g> j"</string>
-    <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
-    <skip />
-    <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
-    <skip />
-    <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
-    <skip />
-    <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium (1465359726485910115) -->
-    <skip />
-    <!-- no translation found for duration_days_medium (5994225628248661388) -->
-    <skip />
-    <!-- no translation found for duration_years_medium (734023884353592526) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_past (893797065424596243) -->
-    <skip />
+    <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g> min geleden"</string>
+    <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g> u geleden"</string>
+    <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g> d geleden"</string>
+    <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g> j geleden"</string>
+    <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> min"</string>
+    <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> u"</string>
+    <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> d"</string>
+    <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> j"</string>
+    <string name="duration_minutes_medium_future" msgid="2750894988731934402">"over <xliff:g id="COUNT">%d</xliff:g> min"</string>
+    <string name="duration_hours_medium_future" msgid="6050833881463849764">"over <xliff:g id="COUNT">%d</xliff:g> u"</string>
+    <string name="duration_days_medium_future" msgid="1700821545602729963">"over <xliff:g id="COUNT">%d</xliff:g> d"</string>
+    <string name="duration_years_medium_future" msgid="3281018940397120166">"over <xliff:g id="COUNT">%d</xliff:g> j"</string>
+    <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g> min geleden"</string>
+    <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g> u geleden"</string>
+    <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g> d geleden"</string>
+    <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g> j geleden"</string>
     <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# minuut geleden}other{# minuten geleden}}"</string>
     <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# uur geleden}other{# uur geleden}}"</string>
     <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# dag geleden}other{# dagen geleden}}"</string>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index 961822a8..9960691 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -1158,38 +1158,22 @@
     <string name="duration_hours_shortest_future" msgid="2979276794547981674">"<xliff:g id="COUNT">%d</xliff:g> ଘଣ୍ଟାରେ"</string>
     <string name="duration_days_shortest_future" msgid="3392722163935571543">"<xliff:g id="COUNT">%d</xliff:g> ଦିନରେ"</string>
     <string name="duration_years_shortest_future" msgid="5537464088352970388">"<xliff:g id="COUNT">%d</xliff:g> ବର୍ଷରେ"</string>
-    <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
-    <skip />
-    <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
-    <skip />
-    <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
-    <skip />
-    <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium (1465359726485910115) -->
-    <skip />
-    <!-- no translation found for duration_days_medium (5994225628248661388) -->
-    <skip />
-    <!-- no translation found for duration_years_medium (734023884353592526) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_past (893797065424596243) -->
-    <skip />
+    <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g> ମିନିଟ ପୂର୍ବେ"</string>
+    <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g> ଘଣ୍ଟା ପୂର୍ବେ"</string>
+    <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g> ଦିନ ପୂର୍ବେ"</string>
+    <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g> ବର୍ଷ ପୂର୍ବେ"</string>
+    <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> ମିନିଟ"</string>
+    <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> ଘଣ୍ଟା"</string>
+    <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> ଦିନ"</string>
+    <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> ବର୍ଷ"</string>
+    <string name="duration_minutes_medium_future" msgid="2750894988731934402">"<xliff:g id="COUNT">%d</xliff:g> ମିନିଟରେ"</string>
+    <string name="duration_hours_medium_future" msgid="6050833881463849764">"<xliff:g id="COUNT">%d</xliff:g> ଘଣ୍ଟାରେ"</string>
+    <string name="duration_days_medium_future" msgid="1700821545602729963">"<xliff:g id="COUNT">%d</xliff:g> ଦିନରେ"</string>
+    <string name="duration_years_medium_future" msgid="3281018940397120166">"<xliff:g id="COUNT">%d</xliff:g> ବର୍ଷରେ"</string>
+    <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g> ମିନିଟ ପୂର୍ବେ"</string>
+    <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g> ଘଣ୍ଟା ପୂର୍ବେ"</string>
+    <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g> ଦିନ ପୂର୍ବେ"</string>
+    <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g> ବର୍ଷ ପୂର୍ବେ"</string>
     <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# ମିନିଟ ପୂର୍ବେ}other{# ମିନିଟ ପୂର୍ବେ}}"</string>
     <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# ଘଣ୍ଟା ପୂର୍ବେ}other{# ଘଣ୍ଟା ପୂର୍ବେ}}"</string>
     <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# ଦିନ ପୂର୍ବେ}other{# ଦିନ ପୂର୍ବେ}}"</string>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index 238354a..6d92e01 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -1158,38 +1158,22 @@
     <string name="duration_hours_shortest_future" msgid="2979276794547981674">"<xliff:g id="COUNT">%d</xliff:g> ਘੰਟੇ ਵਿੱਚ"</string>
     <string name="duration_days_shortest_future" msgid="3392722163935571543">"<xliff:g id="COUNT">%d</xliff:g> ਦਿਨ ਵਿੱਚ"</string>
     <string name="duration_years_shortest_future" msgid="5537464088352970388">"<xliff:g id="COUNT">%d</xliff:g> ਸਾਲ ਵਿੱਚ"</string>
-    <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
-    <skip />
-    <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
-    <skip />
-    <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
-    <skip />
-    <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium (1465359726485910115) -->
-    <skip />
-    <!-- no translation found for duration_days_medium (5994225628248661388) -->
-    <skip />
-    <!-- no translation found for duration_years_medium (734023884353592526) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_past (893797065424596243) -->
-    <skip />
+    <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g> ਮਿੰਟ ਪਹਿਲਾਂ"</string>
+    <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g> ਘੰਟੇ ਪਹਿਲਾਂ"</string>
+    <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g> ਦਿਨ ਪਹਿਲਾਂ"</string>
+    <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g> ਸਾਲ ਪਹਿਲਾਂ"</string>
+    <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> ਮਿੰਟ"</string>
+    <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> ਘੰਟਾ"</string>
+    <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> ਦਿਨ"</string>
+    <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> ਸਾਲ"</string>
+    <string name="duration_minutes_medium_future" msgid="2750894988731934402">"<xliff:g id="COUNT">%d</xliff:g> ਮਿੰਟ ਵਿੱਚ"</string>
+    <string name="duration_hours_medium_future" msgid="6050833881463849764">"<xliff:g id="COUNT">%d</xliff:g> ਘੰਟੇ ਵਿੱਚ"</string>
+    <string name="duration_days_medium_future" msgid="1700821545602729963">"<xliff:g id="COUNT">%d</xliff:g> ਦਿਨ ਵਿੱਚ"</string>
+    <string name="duration_years_medium_future" msgid="3281018940397120166">"<xliff:g id="COUNT">%d</xliff:g> ਸਾਲ ਵਿੱਚ"</string>
+    <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g> ਮਿੰਟ ਪਹਿਲਾਂ"</string>
+    <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g> ਘੰਟਾ ਪਹਿਲਾਂ"</string>
+    <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g> ਦਿਨ ਪਹਿਲਾਂ"</string>
+    <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g> ਸਾਲ ਪਹਿਲਾਂ"</string>
     <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# ਮਿੰਟ ਪਹਿਲਾਂ}one{# ਮਿੰਟ ਪਹਿਲਾਂ}other{# ਮਿੰਟ ਪਹਿਲਾਂ}}"</string>
     <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# ਘੰਟਾ ਪਹਿਲਾਂ}one{# ਘੰਟਾ ਪਹਿਲਾਂ}other{# ਘੰਟੇ ਪਹਿਲਾਂ}}"</string>
     <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# ਦਿਨ ਪਹਿਲਾਂ}one{# ਦਿਨ ਪਹਿਲਾਂ}other{# ਦਿਨ ਪਹਿਲਾਂ}}"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 3d71d7b..ec37251 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -1160,38 +1160,22 @@
     <string name="duration_hours_shortest_future" msgid="2979276794547981674">"za <xliff:g id="COUNT">%d</xliff:g> godz."</string>
     <string name="duration_days_shortest_future" msgid="3392722163935571543">"za <xliff:g id="COUNT">%d</xliff:g> d."</string>
     <string name="duration_years_shortest_future" msgid="5537464088352970388">"za <xliff:g id="COUNT">%d</xliff:g> r."</string>
-    <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
-    <skip />
-    <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
-    <skip />
-    <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
-    <skip />
-    <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium (1465359726485910115) -->
-    <skip />
-    <!-- no translation found for duration_days_medium (5994225628248661388) -->
-    <skip />
-    <!-- no translation found for duration_years_medium (734023884353592526) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_past (893797065424596243) -->
-    <skip />
+    <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g> min temu"</string>
+    <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g> godz. temu"</string>
+    <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g> d temu"</string>
+    <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g> r./l. temu"</string>
+    <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> min"</string>
+    <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> godz."</string>
+    <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> d"</string>
+    <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> r./l."</string>
+    <string name="duration_minutes_medium_future" msgid="2750894988731934402">"za <xliff:g id="COUNT">%d</xliff:g> min"</string>
+    <string name="duration_hours_medium_future" msgid="6050833881463849764">"za <xliff:g id="COUNT">%d</xliff:g> godz."</string>
+    <string name="duration_days_medium_future" msgid="1700821545602729963">"za <xliff:g id="COUNT">%d</xliff:g> d"</string>
+    <string name="duration_years_medium_future" msgid="3281018940397120166">"za <xliff:g id="COUNT">%d</xliff:g> r./l."</string>
+    <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g> min temu"</string>
+    <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g> godz. temu"</string>
+    <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g> d temu"</string>
+    <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g> r./l. temu"</string>
     <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# minutę temu}few{# minuty temu}many{# minut temu}other{# minuty temu}}"</string>
     <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# godzinę temu}few{# godziny temu}many{# godzin temu}other{# godziny temu}}"</string>
     <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# dzień temu}few{# dni temu}many{# dni temu}other{# dnia temu}}"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 79bfcf4..738951e 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -1159,38 +1159,22 @@
     <string name="duration_hours_shortest_future" msgid="2979276794547981674">"în <xliff:g id="COUNT">%d</xliff:g> ore"</string>
     <string name="duration_days_shortest_future" msgid="3392722163935571543">"în <xliff:g id="COUNT">%d</xliff:g> z"</string>
     <string name="duration_years_shortest_future" msgid="5537464088352970388">"în <xliff:g id="COUNT">%d</xliff:g> ani"</string>
-    <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
-    <skip />
-    <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
-    <skip />
-    <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
-    <skip />
-    <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium (1465359726485910115) -->
-    <skip />
-    <!-- no translation found for duration_days_medium (5994225628248661388) -->
-    <skip />
-    <!-- no translation found for duration_years_medium (734023884353592526) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_past (893797065424596243) -->
-    <skip />
+    <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"acum <xliff:g id="COUNT">%d</xliff:g> min."</string>
+    <string name="duration_hours_shortest_past" msgid="2098397414186628489">"acum <xliff:g id="COUNT">%d</xliff:g> h"</string>
+    <string name="duration_days_shortest_past" msgid="1832006037955897625">"acum <xliff:g id="COUNT">%d</xliff:g> z"</string>
+    <string name="duration_years_shortest_past" msgid="6168256514200469291">"acum <xliff:g id="COUNT">%d</xliff:g> a"</string>
+    <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> min."</string>
+    <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> h"</string>
+    <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> z"</string>
+    <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> a"</string>
+    <string name="duration_minutes_medium_future" msgid="2750894988731934402">"peste <xliff:g id="COUNT">%d</xliff:g> min."</string>
+    <string name="duration_hours_medium_future" msgid="6050833881463849764">"peste <xliff:g id="COUNT">%d</xliff:g> h"</string>
+    <string name="duration_days_medium_future" msgid="1700821545602729963">"peste <xliff:g id="COUNT">%d</xliff:g> z"</string>
+    <string name="duration_years_medium_future" msgid="3281018940397120166">"peste <xliff:g id="COUNT">%d</xliff:g> a"</string>
+    <string name="duration_minutes_medium_past" msgid="7400424340181947714">"acum <xliff:g id="COUNT">%d</xliff:g> min."</string>
+    <string name="duration_hours_medium_past" msgid="6709441336035202617">"acum <xliff:g id="COUNT">%d</xliff:g> h"</string>
+    <string name="duration_days_medium_past" msgid="5748156261134344532">"acum <xliff:g id="COUNT">%d</xliff:g> z"</string>
+    <string name="duration_years_medium_past" msgid="893797065424596243">"acum <xliff:g id="COUNT">%d</xliff:g> a"</string>
     <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{Acum # minut}few{Acum # minute}other{Acum # de minute}}"</string>
     <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{Acum # oră}few{Acum # ore}other{Acum # de ore}}"</string>
     <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{Acum # zi}few{Acum # zile}other{Acum # de zile}}"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 2631dab..285e941 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -1160,38 +1160,22 @@
     <string name="duration_hours_shortest_future" msgid="2979276794547981674">"через <xliff:g id="COUNT">%d</xliff:g> ч."</string>
     <string name="duration_days_shortest_future" msgid="3392722163935571543">"через <xliff:g id="COUNT">%d</xliff:g> дн."</string>
     <string name="duration_years_shortest_future" msgid="5537464088352970388">"через <xliff:g id="COUNT">%d</xliff:g> г."</string>
-    <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
-    <skip />
-    <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
-    <skip />
-    <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
-    <skip />
-    <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium (1465359726485910115) -->
-    <skip />
-    <!-- no translation found for duration_days_medium (5994225628248661388) -->
-    <skip />
-    <!-- no translation found for duration_years_medium (734023884353592526) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_past (893797065424596243) -->
-    <skip />
+    <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g> мин. назад"</string>
+    <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g> ч. назад"</string>
+    <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g> дн. назад"</string>
+    <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g> г./лет назад"</string>
+    <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> мин."</string>
+    <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> ч."</string>
+    <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> дн."</string>
+    <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> г./лет"</string>
+    <string name="duration_minutes_medium_future" msgid="2750894988731934402">"через <xliff:g id="COUNT">%d</xliff:g> мин."</string>
+    <string name="duration_hours_medium_future" msgid="6050833881463849764">"через <xliff:g id="COUNT">%d</xliff:g> ч."</string>
+    <string name="duration_days_medium_future" msgid="1700821545602729963">"через <xliff:g id="COUNT">%d</xliff:g> дн."</string>
+    <string name="duration_years_medium_future" msgid="3281018940397120166">"через <xliff:g id="COUNT">%d</xliff:g> г./лет"</string>
+    <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g> мин. назад"</string>
+    <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g> ч. назад"</string>
+    <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g> дн. назад"</string>
+    <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g> г./лет назад"</string>
     <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# минуту назад}one{# минуту назад}few{# минуты назад}many{# минут назад}other{# минуты назад}}"</string>
     <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# час назад}one{# час назад}few{# часа назад}many{# часов назад}other{# часа назад}}"</string>
     <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# день назад}one{# день назад}few{# дня назад}many{# дней назад}other{# дня назад}}"</string>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index 99b925a..0cb6f30 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -1158,38 +1158,22 @@
     <string name="duration_hours_shortest_future" msgid="2979276794547981674">"පැ<xliff:g id="COUNT">%d</xliff:g>කින්"</string>
     <string name="duration_days_shortest_future" msgid="3392722163935571543">"දි<xliff:g id="COUNT">%d</xliff:g>කින්"</string>
     <string name="duration_years_shortest_future" msgid="5537464088352970388">"ව<xliff:g id="COUNT">%d</xliff:g>කින්"</string>
-    <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
-    <skip />
-    <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
-    <skip />
-    <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
-    <skip />
-    <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium (1465359726485910115) -->
-    <skip />
-    <!-- no translation found for duration_days_medium (5994225628248661388) -->
-    <skip />
-    <!-- no translation found for duration_years_medium (734023884353592526) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_past (893797065424596243) -->
-    <skip />
+    <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"මිනි <xliff:g id="COUNT">%d</xliff:g>කට පෙර"</string>
+    <string name="duration_hours_shortest_past" msgid="2098397414186628489">"පැය <xliff:g id="COUNT">%d</xliff:g>කට පෙර"</string>
+    <string name="duration_days_shortest_past" msgid="1832006037955897625">"දින <xliff:g id="COUNT">%d</xliff:g>කට පෙර"</string>
+    <string name="duration_years_shortest_past" msgid="6168256514200469291">"වසර <xliff:g id="COUNT">%d</xliff:g>කට පෙර"</string>
+    <string name="duration_minutes_medium" msgid="5891933490342643944">"මිනි <xliff:g id="COUNT">%d</xliff:g>ක්"</string>
+    <string name="duration_hours_medium" msgid="1465359726485910115">"පැය <xliff:g id="COUNT">%d</xliff:g>ක්"</string>
+    <string name="duration_days_medium" msgid="5994225628248661388">"දින <xliff:g id="COUNT">%d</xliff:g>"</string>
+    <string name="duration_years_medium" msgid="734023884353592526">"වසර <xliff:g id="COUNT">%d</xliff:g>"</string>
+    <string name="duration_minutes_medium_future" msgid="2750894988731934402">"මිනි <xliff:g id="COUNT">%d</xliff:g>කින්"</string>
+    <string name="duration_hours_medium_future" msgid="6050833881463849764">"පැය <xliff:g id="COUNT">%d</xliff:g>කින්"</string>
+    <string name="duration_days_medium_future" msgid="1700821545602729963">"දින <xliff:g id="COUNT">%d</xliff:g>කින්"</string>
+    <string name="duration_years_medium_future" msgid="3281018940397120166">"වසර <xliff:g id="COUNT">%d</xliff:g>කින්"</string>
+    <string name="duration_minutes_medium_past" msgid="7400424340181947714">"මිනි <xliff:g id="COUNT">%d</xliff:g>කට පෙර"</string>
+    <string name="duration_hours_medium_past" msgid="6709441336035202617">"පැය <xliff:g id="COUNT">%d</xliff:g>කට පෙර"</string>
+    <string name="duration_days_medium_past" msgid="5748156261134344532">"දින <xliff:g id="COUNT">%d</xliff:g>කට පෙර"</string>
+    <string name="duration_years_medium_past" msgid="893797065424596243">"වසර <xliff:g id="COUNT">%d</xliff:g>කට පෙර"</string>
     <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{මිනිත්තු #කට පෙර}one{මිනිත්තු #කට පෙර}other{මිනිත්තු #කට පෙර}}"</string>
     <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{පැය #කට පෙර}one{පැය #කට පෙර}other{පැය #කට පෙර}}"</string>
     <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{දින #කට පෙර}one{දින #කට පෙර}other{දින #කට පෙර}}"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 3277781..9d0efef 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -1160,38 +1160,22 @@
     <string name="duration_hours_shortest_future" msgid="2979276794547981674">"o <xliff:g id="COUNT">%d</xliff:g> h"</string>
     <string name="duration_days_shortest_future" msgid="3392722163935571543">"o <xliff:g id="COUNT">%d</xliff:g> d."</string>
     <string name="duration_years_shortest_future" msgid="5537464088352970388">"o <xliff:g id="COUNT">%d</xliff:g> r."</string>
-    <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
-    <skip />
-    <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
-    <skip />
-    <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
-    <skip />
-    <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium (1465359726485910115) -->
-    <skip />
-    <!-- no translation found for duration_days_medium (5994225628248661388) -->
-    <skip />
-    <!-- no translation found for duration_years_medium (734023884353592526) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_past (893797065424596243) -->
-    <skip />
+    <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"Pred <xliff:g id="COUNT">%d</xliff:g> min"</string>
+    <string name="duration_hours_shortest_past" msgid="2098397414186628489">"Pred <xliff:g id="COUNT">%d</xliff:g> h"</string>
+    <string name="duration_days_shortest_past" msgid="1832006037955897625">"Pred <xliff:g id="COUNT">%d</xliff:g> d."</string>
+    <string name="duration_years_shortest_past" msgid="6168256514200469291">"Pred <xliff:g id="COUNT">%d</xliff:g> r."</string>
+    <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> min"</string>
+    <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> h"</string>
+    <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> d."</string>
+    <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> r."</string>
+    <string name="duration_minutes_medium_future" msgid="2750894988731934402">"O <xliff:g id="COUNT">%d</xliff:g> min"</string>
+    <string name="duration_hours_medium_future" msgid="6050833881463849764">"O <xliff:g id="COUNT">%d</xliff:g> h"</string>
+    <string name="duration_days_medium_future" msgid="1700821545602729963">"O <xliff:g id="COUNT">%d</xliff:g> d."</string>
+    <string name="duration_years_medium_future" msgid="3281018940397120166">"O <xliff:g id="COUNT">%d</xliff:g> r."</string>
+    <string name="duration_minutes_medium_past" msgid="7400424340181947714">"Pred <xliff:g id="COUNT">%d</xliff:g> min"</string>
+    <string name="duration_hours_medium_past" msgid="6709441336035202617">"Pred <xliff:g id="COUNT">%d</xliff:g> h"</string>
+    <string name="duration_days_medium_past" msgid="5748156261134344532">"Pred <xliff:g id="COUNT">%d</xliff:g> d."</string>
+    <string name="duration_years_medium_past" msgid="893797065424596243">"Pred <xliff:g id="COUNT">%d</xliff:g> r."</string>
     <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{Pred # minútou}few{Pred # minútami}many{Pred # minúty}other{Pred # minútami}}"</string>
     <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{Pred # hodinou}few{Pred # hodinami}many{Pred # hodiny}other{Pred # hodinami}}"</string>
     <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{Pred # dňom}few{Pred # dňami}many{Pred # dňa}other{Pred # dňami}}"</string>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index 44981bb..744b557 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -1158,38 +1158,22 @@
     <string name="duration_hours_shortest_future" msgid="2979276794547981674">"për <xliff:g id="COUNT">%d</xliff:g> orë"</string>
     <string name="duration_days_shortest_future" msgid="3392722163935571543">"për <xliff:g id="COUNT">%d</xliff:g> ditë"</string>
     <string name="duration_years_shortest_future" msgid="5537464088352970388">"për <xliff:g id="COUNT">%d</xliff:g> vit"</string>
-    <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
-    <skip />
-    <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
-    <skip />
-    <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
-    <skip />
-    <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium (1465359726485910115) -->
-    <skip />
-    <!-- no translation found for duration_days_medium (5994225628248661388) -->
-    <skip />
-    <!-- no translation found for duration_years_medium (734023884353592526) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_past (893797065424596243) -->
-    <skip />
+    <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g> min. më parë"</string>
+    <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g> orë më parë"</string>
+    <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g> ditë më parë"</string>
+    <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g> vite më parë"</string>
+    <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> min."</string>
+    <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> orë"</string>
+    <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> ditë"</string>
+    <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> vite"</string>
+    <string name="duration_minutes_medium_future" msgid="2750894988731934402">"për <xliff:g id="COUNT">%d</xliff:g> min."</string>
+    <string name="duration_hours_medium_future" msgid="6050833881463849764">"për <xliff:g id="COUNT">%d</xliff:g> orë"</string>
+    <string name="duration_days_medium_future" msgid="1700821545602729963">"për <xliff:g id="COUNT">%d</xliff:g> ditë"</string>
+    <string name="duration_years_medium_future" msgid="3281018940397120166">"për <xliff:g id="COUNT">%d</xliff:g> vite"</string>
+    <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g> min. më parë"</string>
+    <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g> orë më parë"</string>
+    <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g> ditë më parë"</string>
+    <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g> vite më parë"</string>
     <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# minutë më parë}other{# minuta më parë}}"</string>
     <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# orë më parë}other{# orë më parë}}"</string>
     <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# ditë më parë}other{# ditë më parë}}"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index cc2248c..45fb8ee 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -1159,38 +1159,22 @@
     <string name="duration_hours_shortest_future" msgid="2979276794547981674">"за <xliff:g id="COUNT">%d</xliff:g> с"</string>
     <string name="duration_days_shortest_future" msgid="3392722163935571543">"за <xliff:g id="COUNT">%d</xliff:g> д"</string>
     <string name="duration_years_shortest_future" msgid="5537464088352970388">"за <xliff:g id="COUNT">%d</xliff:g> год"</string>
-    <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
-    <skip />
-    <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
-    <skip />
-    <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
-    <skip />
-    <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium (1465359726485910115) -->
-    <skip />
-    <!-- no translation found for duration_days_medium (5994225628248661388) -->
-    <skip />
-    <!-- no translation found for duration_years_medium (734023884353592526) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_past (893797065424596243) -->
-    <skip />
+    <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"пре <xliff:g id="COUNT">%d</xliff:g> мин"</string>
+    <string name="duration_hours_shortest_past" msgid="2098397414186628489">"пре <xliff:g id="COUNT">%d</xliff:g> с"</string>
+    <string name="duration_days_shortest_past" msgid="1832006037955897625">"пре <xliff:g id="COUNT">%d</xliff:g> д"</string>
+    <string name="duration_years_shortest_past" msgid="6168256514200469291">"пре <xliff:g id="COUNT">%d</xliff:g> г"</string>
+    <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> мин"</string>
+    <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> с"</string>
+    <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> д"</string>
+    <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> год"</string>
+    <string name="duration_minutes_medium_future" msgid="2750894988731934402">"за <xliff:g id="COUNT">%d</xliff:g> мин"</string>
+    <string name="duration_hours_medium_future" msgid="6050833881463849764">"за <xliff:g id="COUNT">%d</xliff:g> с"</string>
+    <string name="duration_days_medium_future" msgid="1700821545602729963">"за <xliff:g id="COUNT">%d</xliff:g> д"</string>
+    <string name="duration_years_medium_future" msgid="3281018940397120166">"за <xliff:g id="COUNT">%d</xliff:g> год"</string>
+    <string name="duration_minutes_medium_past" msgid="7400424340181947714">"пре <xliff:g id="COUNT">%d</xliff:g> мин"</string>
+    <string name="duration_hours_medium_past" msgid="6709441336035202617">"пре <xliff:g id="COUNT">%d</xliff:g> с"</string>
+    <string name="duration_days_medium_past" msgid="5748156261134344532">"пре <xliff:g id="COUNT">%d</xliff:g> д"</string>
+    <string name="duration_years_medium_past" msgid="893797065424596243">"пре <xliff:g id="COUNT">%d</xliff:g> год"</string>
     <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{Пре # минут}one{Пре # минут}few{Пре # минута}other{Пре # минута}}"</string>
     <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{Пре # сат}one{Пре # сат}few{Пре # сата}other{Пре # сати}}"</string>
     <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{Пре # дан}one{Пре # дан}few{Пре # дана}other{Пре # дана}}"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index a7d4d86..d6565bf 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -1158,38 +1158,22 @@
     <string name="duration_hours_shortest_future" msgid="2979276794547981674">"baada ya saa <xliff:g id="COUNT">%d</xliff:g>"</string>
     <string name="duration_days_shortest_future" msgid="3392722163935571543">"baada ya siku <xliff:g id="COUNT">%d</xliff:g>"</string>
     <string name="duration_years_shortest_future" msgid="5537464088352970388">"baada ya mwaka <xliff:g id="COUNT">%d</xliff:g>"</string>
-    <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
-    <skip />
-    <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
-    <skip />
-    <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
-    <skip />
-    <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium (1465359726485910115) -->
-    <skip />
-    <!-- no translation found for duration_days_medium (5994225628248661388) -->
-    <skip />
-    <!-- no translation found for duration_years_medium (734023884353592526) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_past (893797065424596243) -->
-    <skip />
+    <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"dak <xliff:g id="COUNT">%d</xliff:g> zilizopita"</string>
+    <string name="duration_hours_shortest_past" msgid="2098397414186628489">"saa <xliff:g id="COUNT">%d</xliff:g> zilizopita"</string>
+    <string name="duration_days_shortest_past" msgid="1832006037955897625">"siku <xliff:g id="COUNT">%d</xliff:g> zilizopita"</string>
+    <string name="duration_years_shortest_past" msgid="6168256514200469291">"miaka <xliff:g id="COUNT">%d</xliff:g> iliyopita"</string>
+    <string name="duration_minutes_medium" msgid="5891933490342643944">"dak <xliff:g id="COUNT">%d</xliff:g>"</string>
+    <string name="duration_hours_medium" msgid="1465359726485910115">"saa <xliff:g id="COUNT">%d</xliff:g>"</string>
+    <string name="duration_days_medium" msgid="5994225628248661388">"siku <xliff:g id="COUNT">%d</xliff:g>"</string>
+    <string name="duration_years_medium" msgid="734023884353592526">"miaka <xliff:g id="COUNT">%d</xliff:g>"</string>
+    <string name="duration_minutes_medium_future" msgid="2750894988731934402">"ndani ya dak <xliff:g id="COUNT">%d</xliff:g>"</string>
+    <string name="duration_hours_medium_future" msgid="6050833881463849764">"ndani ya saa <xliff:g id="COUNT">%d</xliff:g>"</string>
+    <string name="duration_days_medium_future" msgid="1700821545602729963">"ndani ya siku <xliff:g id="COUNT">%d</xliff:g>"</string>
+    <string name="duration_years_medium_future" msgid="3281018940397120166">"ndani ya miaka <xliff:g id="COUNT">%d</xliff:g>"</string>
+    <string name="duration_minutes_medium_past" msgid="7400424340181947714">"dak <xliff:g id="COUNT">%d</xliff:g> zilizopita"</string>
+    <string name="duration_hours_medium_past" msgid="6709441336035202617">"saa <xliff:g id="COUNT">%d</xliff:g> zilizopita"</string>
+    <string name="duration_days_medium_past" msgid="5748156261134344532">"siku <xliff:g id="COUNT">%d</xliff:g> zilizopita"</string>
+    <string name="duration_years_medium_past" msgid="893797065424596243">"miaka <xliff:g id="COUNT">%d</xliff:g> iliyopita"</string>
     <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{Dakika # iliyopita}other{Dakika # zilizopita}}"</string>
     <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{Saa # iliyopita}other{Saa # zilizopita}}"</string>
     <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{Siku # iliyopita}other{Siku # zilizopita}}"</string>
diff --git a/core/res/res/values-sw600dp/config.xml b/core/res/res/values-sw600dp/config.xml
index 34b6a54..0b0a4cb 100644
--- a/core/res/res/values-sw600dp/config.xml
+++ b/core/res/res/values-sw600dp/config.xml
@@ -47,7 +47,7 @@
     <bool name="config_navBarCanMove">false</bool>
 
     <!-- Set to true to enable the user switcher on the keyguard. -->
-    <bool name="config_keyguardUserSwitcher">true</bool>
+    <bool name="config_keyguardUserSwitcher">false</bool>
 
     <!-- If true, show multiuser switcher by default unless the user specifically disables it. -->
     <bool name="config_showUserSwitcherByDefault">true</bool>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index 6107dbe..17119a6 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -1158,38 +1158,22 @@
     <string name="duration_hours_shortest_future" msgid="2979276794547981674">"<xliff:g id="COUNT">%d</xliff:g> மணிநேரத்தில்"</string>
     <string name="duration_days_shortest_future" msgid="3392722163935571543">"<xliff:g id="COUNT">%d</xliff:g> நாட்களில்"</string>
     <string name="duration_years_shortest_future" msgid="5537464088352970388">"<xliff:g id="COUNT">%d</xliff:g> ஆண்டில்"</string>
-    <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
-    <skip />
-    <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
-    <skip />
-    <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
-    <skip />
-    <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium (1465359726485910115) -->
-    <skip />
-    <!-- no translation found for duration_days_medium (5994225628248661388) -->
-    <skip />
-    <!-- no translation found for duration_years_medium (734023884353592526) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_past (893797065424596243) -->
-    <skip />
+    <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g> நி முன்பு"</string>
+    <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g> ம.நே முன்பு"</string>
+    <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g> நா முன்பு"</string>
+    <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g> ஆண்டு முன்பு"</string>
+    <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> நிமிடம்"</string>
+    <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> ம.நே"</string>
+    <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> நா"</string>
+    <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> ஆண்டு"</string>
+    <string name="duration_minutes_medium_future" msgid="2750894988731934402">"<xliff:g id="COUNT">%d</xliff:g> நிமிடத்தில்"</string>
+    <string name="duration_hours_medium_future" msgid="6050833881463849764">"<xliff:g id="COUNT">%d</xliff:g> மணிநேரத்தில்"</string>
+    <string name="duration_days_medium_future" msgid="1700821545602729963">"<xliff:g id="COUNT">%d</xliff:g> நாளில்"</string>
+    <string name="duration_years_medium_future" msgid="3281018940397120166">"<xliff:g id="COUNT">%d</xliff:g> ஆண்டில்"</string>
+    <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g> நிமிடம் முன்பு"</string>
+    <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g> ம.நே முன்பு"</string>
+    <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g> நா முன்பு"</string>
+    <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g> ஆண்டுக்கு முன்பு"</string>
     <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# நிமிடத்திற்கு முன்பு}other{# நிமிடங்களுக்கு முன்பு}}"</string>
     <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# மணிநேரத்திற்கு முன்பு}other{# மணிநேரத்திற்கு முன்பு}}"</string>
     <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# நாளுக்கு முன்பு}other{# நாட்களுக்கு முன்பு}}"</string>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index 53ef42a..c2be30e 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -1158,38 +1158,22 @@
     <string name="duration_hours_shortest_future" msgid="2979276794547981674">"<xliff:g id="COUNT">%d</xliff:g>గంటలో"</string>
     <string name="duration_days_shortest_future" msgid="3392722163935571543">"<xliff:g id="COUNT">%d</xliff:g>రోజులో"</string>
     <string name="duration_years_shortest_future" msgid="5537464088352970388">"<xliff:g id="COUNT">%d</xliff:g>సంవత్సరంలో"</string>
-    <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
-    <skip />
-    <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
-    <skip />
-    <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
-    <skip />
-    <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium (1465359726485910115) -->
-    <skip />
-    <!-- no translation found for duration_days_medium (5994225628248661388) -->
-    <skip />
-    <!-- no translation found for duration_years_medium (734023884353592526) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_past (893797065424596243) -->
-    <skip />
+    <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g>నిమిషం క్రితం"</string>
+    <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g>గంట క్రితం"</string>
+    <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g>రోజు క్రితం"</string>
+    <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g>సం క్రితం"</string>
+    <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g>నిమిషం"</string>
+    <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g>గంట"</string>
+    <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g>రోజు"</string>
+    <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g>సంవత్సరం"</string>
+    <string name="duration_minutes_medium_future" msgid="2750894988731934402">"<xliff:g id="COUNT">%d</xliff:g>నిమిషాలలో"</string>
+    <string name="duration_hours_medium_future" msgid="6050833881463849764">"<xliff:g id="COUNT">%d</xliff:g>గంటలో"</string>
+    <string name="duration_days_medium_future" msgid="1700821545602729963">"<xliff:g id="COUNT">%d</xliff:g>రోజులో"</string>
+    <string name="duration_years_medium_future" msgid="3281018940397120166">"<xliff:g id="COUNT">%d</xliff:g>సంవత్సరంలో"</string>
+    <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g>నిమిషాల క్రితం"</string>
+    <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g>గంట క్రితం"</string>
+    <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g>రోజు క్రితం"</string>
+    <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g>సంవత్సరం క్రితం"</string>
     <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# నిమిషం క్రితం}other{# నిమిషాల క్రితం}}"</string>
     <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# గంట క్రితం}other{# గంటల క్రితం}}"</string>
     <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# రోజు క్రితం}other{# రోజుల క్రితం}}"</string>
@@ -2130,7 +2114,7 @@
     <string name="autofill_save_no" msgid="9212826374207023544">"వద్దు, ధన్యవాదాలు"</string>
     <string name="autofill_save_notnow" msgid="2853932672029024195">"ఇప్పుడు కాదు"</string>
     <string name="autofill_save_never" msgid="6821841919831402526">"ఎప్పుడూ వద్దు"</string>
-    <string name="autofill_update_yes" msgid="4608662968996874445">"అప్‌డేట్ చేయి"</string>
+    <string name="autofill_update_yes" msgid="4608662968996874445">"అప్‌డేట్ చేయండి"</string>
     <string name="autofill_continue_yes" msgid="7914985605534510385">"కొనసాగించండి"</string>
     <string name="autofill_save_type_password" msgid="5624528786144539944">"పాస్‌వర్డ్"</string>
     <string name="autofill_save_type_address" msgid="3111006395818252885">"అడ్రస్‌"</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 43b1423..7e70a3b 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -1158,38 +1158,22 @@
     <string name="duration_hours_shortest_future" msgid="2979276794547981674">"ใน <xliff:g id="COUNT">%d</xliff:g> ชม."</string>
     <string name="duration_days_shortest_future" msgid="3392722163935571543">"ใน <xliff:g id="COUNT">%d</xliff:g> วัน"</string>
     <string name="duration_years_shortest_future" msgid="5537464088352970388">"ใน <xliff:g id="COUNT">%d</xliff:g> ปี"</string>
-    <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
-    <skip />
-    <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
-    <skip />
-    <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
-    <skip />
-    <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium (1465359726485910115) -->
-    <skip />
-    <!-- no translation found for duration_days_medium (5994225628248661388) -->
-    <skip />
-    <!-- no translation found for duration_years_medium (734023884353592526) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_past (893797065424596243) -->
-    <skip />
+    <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g> นาทีที่ผ่านมา"</string>
+    <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g> ชม. ที่ผ่านมา"</string>
+    <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g> วันที่ผ่านมา"</string>
+    <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g> ปีที่ผ่านมา"</string>
+    <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> นาที"</string>
+    <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> ชม."</string>
+    <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> วัน"</string>
+    <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> ปี"</string>
+    <string name="duration_minutes_medium_future" msgid="2750894988731934402">"ใน <xliff:g id="COUNT">%d</xliff:g> นาที"</string>
+    <string name="duration_hours_medium_future" msgid="6050833881463849764">"ใน <xliff:g id="COUNT">%d</xliff:g> ชม."</string>
+    <string name="duration_days_medium_future" msgid="1700821545602729963">"ใน <xliff:g id="COUNT">%d</xliff:g> วัน"</string>
+    <string name="duration_years_medium_future" msgid="3281018940397120166">"ใน <xliff:g id="COUNT">%d</xliff:g> ปี"</string>
+    <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g> นาทีที่ผ่านมา"</string>
+    <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g> ชม. ที่ผ่านมา"</string>
+    <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g> วันที่ผ่านมา"</string>
+    <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g> ปีที่ผ่านมา"</string>
     <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# นาทีที่ผ่านมา}other{# นาทีที่ผ่านมา}}"</string>
     <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# ชั่วโมงที่ผ่านมา}other{# ชั่วโมงที่ผ่านมา}}"</string>
     <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# วันที่ผ่านมา}other{# วันที่ผ่านมา}}"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 1dbd00b..6fd93bc 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -1158,38 +1158,22 @@
     <string name="duration_hours_shortest_future" msgid="2979276794547981674">"sa <xliff:g id="COUNT">%d</xliff:g>h"</string>
     <string name="duration_days_shortest_future" msgid="3392722163935571543">"sa <xliff:g id="COUNT">%d</xliff:g>d"</string>
     <string name="duration_years_shortest_future" msgid="5537464088352970388">"sa <xliff:g id="COUNT">%d</xliff:g>y"</string>
-    <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
-    <skip />
-    <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
-    <skip />
-    <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
-    <skip />
-    <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium (1465359726485910115) -->
-    <skip />
-    <!-- no translation found for duration_days_medium (5994225628248661388) -->
-    <skip />
-    <!-- no translation found for duration_years_medium (734023884353592526) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_past (893797065424596243) -->
-    <skip />
+    <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g>m nakalipas"</string>
+    <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g>h nakalipas"</string>
+    <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g>d nakalipas"</string>
+    <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g>y nakalipas"</string>
+    <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> min"</string>
+    <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g>hr"</string>
+    <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g>d"</string>
+    <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g>yr"</string>
+    <string name="duration_minutes_medium_future" msgid="2750894988731934402">"sa <xliff:g id="COUNT">%d</xliff:g> (na) min"</string>
+    <string name="duration_hours_medium_future" msgid="6050833881463849764">"sa loob ng <xliff:g id="COUNT">%d</xliff:g>hr"</string>
+    <string name="duration_days_medium_future" msgid="1700821545602729963">"sa <xliff:g id="COUNT">%d</xliff:g>d"</string>
+    <string name="duration_years_medium_future" msgid="3281018940397120166">"sa <xliff:g id="COUNT">%d</xliff:g>yr"</string>
+    <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g>min ang nakalipas"</string>
+    <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g>hr ang nakalipas"</string>
+    <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g>d ang nakalipas"</string>
+    <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g>yr ang nakalipas"</string>
     <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# minuto ang nakalipas}one{# minuto ang nakalipas}other{# na minuto ang nakalipas}}"</string>
     <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# oras ang nakalipas}one{# oras ang nakalipas}other{# na oras ang nakalipas}}"</string>
     <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# araw ang nakalipas}one{# araw ang nakalipas}other{# na araw ang nakalipas}}"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 3777f04..16915b5 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -1158,38 +1158,22 @@
     <string name="duration_hours_shortest_future" msgid="2979276794547981674">"<xliff:g id="COUNT">%d</xliff:g> saat içinde"</string>
     <string name="duration_days_shortest_future" msgid="3392722163935571543">"<xliff:g id="COUNT">%d</xliff:g> gün içinde"</string>
     <string name="duration_years_shortest_future" msgid="5537464088352970388">"<xliff:g id="COUNT">%d</xliff:g> yıl içinde"</string>
-    <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
-    <skip />
-    <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
-    <skip />
-    <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
-    <skip />
-    <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium (1465359726485910115) -->
-    <skip />
-    <!-- no translation found for duration_days_medium (5994225628248661388) -->
-    <skip />
-    <!-- no translation found for duration_years_medium (734023884353592526) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_past (893797065424596243) -->
-    <skip />
+    <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g> dk. önce"</string>
+    <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g> sa. önce"</string>
+    <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g> gün önce"</string>
+    <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g> yıl önce"</string>
+    <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> dk."</string>
+    <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> sa."</string>
+    <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> gün"</string>
+    <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> yıl"</string>
+    <string name="duration_minutes_medium_future" msgid="2750894988731934402">"<xliff:g id="COUNT">%d</xliff:g> dk. içinde"</string>
+    <string name="duration_hours_medium_future" msgid="6050833881463849764">"<xliff:g id="COUNT">%d</xliff:g> sa. içinde"</string>
+    <string name="duration_days_medium_future" msgid="1700821545602729963">"<xliff:g id="COUNT">%d</xliff:g> gün içinde"</string>
+    <string name="duration_years_medium_future" msgid="3281018940397120166">"<xliff:g id="COUNT">%d</xliff:g> yıl içinde"</string>
+    <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g> dk. önce"</string>
+    <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g> sa. önce"</string>
+    <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g> gün önce"</string>
+    <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g> yıl önce"</string>
     <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# dakika önce}other{# dakika önce}}"</string>
     <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# saat önce}other{# saat önce}}"</string>
     <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# gün önce}other{# gün önce}}"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 3d603e3..dcf15a0 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -1160,38 +1160,22 @@
     <string name="duration_hours_shortest_future" msgid="2979276794547981674">"через <xliff:g id="COUNT">%d</xliff:g> год"</string>
     <string name="duration_days_shortest_future" msgid="3392722163935571543">"через <xliff:g id="COUNT">%d</xliff:g> дн."</string>
     <string name="duration_years_shortest_future" msgid="5537464088352970388">"через <xliff:g id="COUNT">%d</xliff:g> р."</string>
-    <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
-    <skip />
-    <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
-    <skip />
-    <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
-    <skip />
-    <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium (1465359726485910115) -->
-    <skip />
-    <!-- no translation found for duration_days_medium (5994225628248661388) -->
-    <skip />
-    <!-- no translation found for duration_years_medium (734023884353592526) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_past (893797065424596243) -->
-    <skip />
+    <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g> хв тому"</string>
+    <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g> год тому"</string>
+    <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g> дн. тому"</string>
+    <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g> р. тому"</string>
+    <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> хв"</string>
+    <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> год"</string>
+    <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> дн."</string>
+    <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> р."</string>
+    <string name="duration_minutes_medium_future" msgid="2750894988731934402">"через <xliff:g id="COUNT">%d</xliff:g> хв"</string>
+    <string name="duration_hours_medium_future" msgid="6050833881463849764">"через <xliff:g id="COUNT">%d</xliff:g> год"</string>
+    <string name="duration_days_medium_future" msgid="1700821545602729963">"через <xliff:g id="COUNT">%d</xliff:g> дн."</string>
+    <string name="duration_years_medium_future" msgid="3281018940397120166">"через <xliff:g id="COUNT">%d</xliff:g> р."</string>
+    <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g> хв тому"</string>
+    <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g> год тому"</string>
+    <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g> дн. тому"</string>
+    <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g> р. тому"</string>
     <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# хвилину тому}one{# хвилину тому}few{# хвилини тому}many{# хвилин тому}other{# хвилини тому}}"</string>
     <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# годину тому}one{# годину тому}few{# години тому}many{# годин тому}other{# години тому}}"</string>
     <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# день тому}one{# день тому}few{# дні тому}many{# днів тому}other{# дня тому}}"</string>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index 8a9c661..8c75e81 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -1158,38 +1158,22 @@
     <string name="duration_hours_shortest_future" msgid="2979276794547981674">"<xliff:g id="COUNT">%d</xliff:g> گھنٹے میں"</string>
     <string name="duration_days_shortest_future" msgid="3392722163935571543">"<xliff:g id="COUNT">%d</xliff:g> دن میں"</string>
     <string name="duration_years_shortest_future" msgid="5537464088352970388">"<xliff:g id="COUNT">%d</xliff:g> سال میں"</string>
-    <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
-    <skip />
-    <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
-    <skip />
-    <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
-    <skip />
-    <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium (1465359726485910115) -->
-    <skip />
-    <!-- no translation found for duration_days_medium (5994225628248661388) -->
-    <skip />
-    <!-- no translation found for duration_years_medium (734023884353592526) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_past (893797065424596243) -->
-    <skip />
+    <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"‫<xliff:g id="COUNT">%d</xliff:g> منٹ قبل"</string>
+    <string name="duration_hours_shortest_past" msgid="2098397414186628489">"‫<xliff:g id="COUNT">%d</xliff:g> گھنٹہ قبل"</string>
+    <string name="duration_days_shortest_past" msgid="1832006037955897625">"‫<xliff:g id="COUNT">%d</xliff:g> دن قبل"</string>
+    <string name="duration_years_shortest_past" msgid="6168256514200469291">"‫<xliff:g id="COUNT">%d</xliff:g> سال قبل"</string>
+    <string name="duration_minutes_medium" msgid="5891933490342643944">"‫<xliff:g id="COUNT">%d</xliff:g> منٹ"</string>
+    <string name="duration_hours_medium" msgid="1465359726485910115">"‫<xliff:g id="COUNT">%d</xliff:g> گھنٹہ"</string>
+    <string name="duration_days_medium" msgid="5994225628248661388">"‫<xliff:g id="COUNT">%d</xliff:g> دن"</string>
+    <string name="duration_years_medium" msgid="734023884353592526">"‫<xliff:g id="COUNT">%d</xliff:g> سال"</string>
+    <string name="duration_minutes_medium_future" msgid="2750894988731934402">"‫<xliff:g id="COUNT">%d</xliff:g> منٹ میں"</string>
+    <string name="duration_hours_medium_future" msgid="6050833881463849764">"‫<xliff:g id="COUNT">%d</xliff:g> گھنٹے میں"</string>
+    <string name="duration_days_medium_future" msgid="1700821545602729963">"‫<xliff:g id="COUNT">%d</xliff:g> دن میں"</string>
+    <string name="duration_years_medium_future" msgid="3281018940397120166">"‫<xliff:g id="COUNT">%d</xliff:g> سال میں"</string>
+    <string name="duration_minutes_medium_past" msgid="7400424340181947714">"‫<xliff:g id="COUNT">%d</xliff:g> منٹ قبل"</string>
+    <string name="duration_hours_medium_past" msgid="6709441336035202617">"‫<xliff:g id="COUNT">%d</xliff:g> گھنٹہ قبل"</string>
+    <string name="duration_days_medium_past" msgid="5748156261134344532">"‫<xliff:g id="COUNT">%d</xliff:g> دن قبل"</string>
+    <string name="duration_years_medium_past" msgid="893797065424596243">"‫<xliff:g id="COUNT">%d</xliff:g> سال قبل"</string>
     <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# منٹ پہلے}other{# منٹ پہلے}}"</string>
     <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# گھنٹہ پہلے}other{# گھنٹے پہلے}}"</string>
     <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# دن پہلے}other{# دن پہلے}}"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index f42e46f..ad0f42d 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -1158,38 +1158,22 @@
     <string name="duration_hours_shortest_future" msgid="2979276794547981674">"<xliff:g id="COUNT">%d</xliff:g> giờ nữa"</string>
     <string name="duration_days_shortest_future" msgid="3392722163935571543">"<xliff:g id="COUNT">%d</xliff:g> ngày nữa"</string>
     <string name="duration_years_shortest_future" msgid="5537464088352970388">"<xliff:g id="COUNT">%d</xliff:g> năm nữa"</string>
-    <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
-    <skip />
-    <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
-    <skip />
-    <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
-    <skip />
-    <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium (1465359726485910115) -->
-    <skip />
-    <!-- no translation found for duration_days_medium (5994225628248661388) -->
-    <skip />
-    <!-- no translation found for duration_years_medium (734023884353592526) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_past (893797065424596243) -->
-    <skip />
+    <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g> phút trước"</string>
+    <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g> giờ trước"</string>
+    <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g> ngày trước"</string>
+    <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g> năm trước"</string>
+    <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> phút"</string>
+    <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> giờ"</string>
+    <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> ngày"</string>
+    <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> năm"</string>
+    <string name="duration_minutes_medium_future" msgid="2750894988731934402">"<xliff:g id="COUNT">%d</xliff:g> phút nữa"</string>
+    <string name="duration_hours_medium_future" msgid="6050833881463849764">"<xliff:g id="COUNT">%d</xliff:g> giờ nữa"</string>
+    <string name="duration_days_medium_future" msgid="1700821545602729963">"<xliff:g id="COUNT">%d</xliff:g> ngày nữa"</string>
+    <string name="duration_years_medium_future" msgid="3281018940397120166">"<xliff:g id="COUNT">%d</xliff:g> năm nữa"</string>
+    <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g> phút trước"</string>
+    <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g> giờ trước"</string>
+    <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g> ngày trước"</string>
+    <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g> năm trước"</string>
     <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# phút trước}other{# phút trước}}"</string>
     <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# giờ trước}other{# giờ trước}}"</string>
     <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# ngày trước}other{# ngày trước}}"</string>
diff --git a/core/res/res/values-w192dp/dimens_material.xml b/core/res/res/values-w192dp/dimens_material.xml
index 797bf5a..a11eb7f 100644
--- a/core/res/res/values-w192dp/dimens_material.xml
+++ b/core/res/res/values-w192dp/dimens_material.xml
@@ -14,7 +14,11 @@
      limitations under the License.
 -->
 <resources>
+    <dimen name="screen_percentage_0416">7.99dp</dimen>
     <dimen name="screen_percentage_05">9.6dp</dimen>
+    <dimen name="screen_percentage_052">9.98dp</dimen>
     <dimen name="screen_percentage_10">19.2dp</dimen>
+    <dimen name="screen_percentage_12">23.04dp</dimen>
     <dimen name="screen_percentage_15">28.8dp</dimen>
+    <dimen name="screen_percentage_3646">69.99dp</dimen>
 </resources>
diff --git a/core/res/res/values-w195dp/dimens_material.xml b/core/res/res/values-w195dp/dimens_material.xml
index 7f3ad29..346066f 100644
--- a/core/res/res/values-w195dp/dimens_material.xml
+++ b/core/res/res/values-w195dp/dimens_material.xml
@@ -14,7 +14,11 @@
      limitations under the License.
 -->
 <resources>
+    <dimen name="screen_percentage_0416">8.11dp</dimen>
     <dimen name="screen_percentage_05">9.75dp</dimen>
+    <dimen name="screen_percentage_052">10.14dp</dimen>
     <dimen name="screen_percentage_10">19.5dp</dimen>
+    <dimen name="screen_percentage_12">23.4dp</dimen>
     <dimen name="screen_percentage_15">29.25dp</dimen>
+    <dimen name="screen_percentage_3646">71.09dp</dimen>
 </resources>
diff --git a/core/res/res/values-w198dp/dimens_material.xml b/core/res/res/values-w198dp/dimens_material.xml
index a8aed25..4c88f05 100644
--- a/core/res/res/values-w198dp/dimens_material.xml
+++ b/core/res/res/values-w198dp/dimens_material.xml
@@ -14,7 +14,11 @@
      limitations under the License.
 -->
 <resources>
+    <dimen name="screen_percentage_0416">8.24dp</dimen>
     <dimen name="screen_percentage_05">9.9dp</dimen>
+    <dimen name="screen_percentage_052">10.3dp</dimen>
     <dimen name="screen_percentage_10">19.8dp</dimen>
+    <dimen name="screen_percentage_12">23.76dp</dimen>
     <dimen name="screen_percentage_15">29.7dp</dimen>
+    <dimen name="screen_percentage_3646">72.1dp</dimen>
 </resources>
diff --git a/core/res/res/values-w204dp-round-watch/dimens_material.xml b/core/res/res/values-w204dp-round-watch/dimens_material.xml
index c07d5c4..54bb0c9 100644
--- a/core/res/res/values-w204dp-round-watch/dimens_material.xml
+++ b/core/res/res/values-w204dp-round-watch/dimens_material.xml
@@ -14,7 +14,11 @@
      limitations under the License.
 -->
 <resources>
+    <dimen name="screen_percentage_0416">8.48dp</dimen>
     <dimen name="screen_percentage_05">10.2dp</dimen>
+    <dimen name="screen_percentage_052">10.61dp</dimen>
     <dimen name="screen_percentage_10">20.4dp</dimen>
+    <dimen name="screen_percentage_12">24.48dp</dimen>
     <dimen name="screen_percentage_15">30.6dp</dimen>
+    <dimen name="screen_percentage_3646">74.42dp</dimen>
 </resources>
diff --git a/core/res/res/values-w205dp/dimens_material.xml b/core/res/res/values-w205dp/dimens_material.xml
index 94907ee..60f65bb 100644
--- a/core/res/res/values-w205dp/dimens_material.xml
+++ b/core/res/res/values-w205dp/dimens_material.xml
@@ -14,7 +14,11 @@
      limitations under the License.
 -->
 <resources>
+    <dimen name="screen_percentage_0416">8.52dp</dimen>
     <dimen name="screen_percentage_05">10.25dp</dimen>
+    <dimen name="screen_percentage_052">10.66dp</dimen>
     <dimen name="screen_percentage_10">20.5dp</dimen>
+    <dimen name="screen_percentage_12">24.6dp</dimen>
     <dimen name="screen_percentage_15">30.75dp</dimen>
+    <dimen name="screen_percentage_3646">74.78dp</dimen>
 </resources>
diff --git a/core/res/res/values-w208dp/dimens_material.xml b/core/res/res/values-w208dp/dimens_material.xml
index 069eeb0..7f4ccd9 100644
--- a/core/res/res/values-w208dp/dimens_material.xml
+++ b/core/res/res/values-w208dp/dimens_material.xml
@@ -14,7 +14,11 @@
      limitations under the License.
 -->
 <resources>
+    <dimen name="screen_percentage_0416">8.65dp</dimen>
     <dimen name="screen_percentage_05">10.4dp</dimen>
+    <dimen name="screen_percentage_052">10.82dp</dimen>
     <dimen name="screen_percentage_10">20.8dp</dimen>
+    <dimen name="screen_percentage_12">24.96dp</dimen>
     <dimen name="screen_percentage_15">31.2dp</dimen>
+    <dimen name="screen_percentage_3646">75.65dp</dimen>
 </resources>
diff --git a/core/res/res/values-w210dp-round-watch/dimens_material.xml b/core/res/res/values-w210dp-round-watch/dimens_material.xml
index 79acf84..ca0889e 100644
--- a/core/res/res/values-w210dp-round-watch/dimens_material.xml
+++ b/core/res/res/values-w210dp-round-watch/dimens_material.xml
@@ -14,6 +14,14 @@
      limitations under the License.
 -->
 <resources>
+    <dimen name="screen_percentage_0416">8.73dp</dimen>
+    <dimen name="screen_percentage_05">10.5dp</dimen>
+    <dimen name="screen_percentage_052">10.92dp</dimen>
+    <dimen name="screen_percentage_10">21dp</dimen>
+    <dimen name="screen_percentage_12">25.2dp</dimen>
+    <dimen name="screen_percentage_15">31.5dp</dimen>
+    <dimen name="screen_percentage_3646">76.57dp</dimen>
+
     <dimen name="text_size_display_4_material">80sp</dimen>
     <dimen name="text_size_display_3_material">50sp</dimen>
     <dimen name="text_size_display_2_material">40sp</dimen>
diff --git a/core/res/res/values-w211dp/dimens_material.xml b/core/res/res/values-w211dp/dimens_material.xml
index bd7ca9a..c483e45 100644
--- a/core/res/res/values-w211dp/dimens_material.xml
+++ b/core/res/res/values-w211dp/dimens_material.xml
@@ -14,7 +14,11 @@
      limitations under the License.
 -->
 <resources>
+    <dimen name="screen_percentage_0416">8.77dp</dimen>
     <dimen name="screen_percentage_05">10.55dp</dimen>
+    <dimen name="screen_percentage_052">10.97dp</dimen>
     <dimen name="screen_percentage_10">21.1dp</dimen>
+    <dimen name="screen_percentage_12">25.32dp</dimen>
     <dimen name="screen_percentage_15">31.65dp</dimen>
+    <dimen name="screen_percentage_3646">76.93dp</dimen>
 </resources>
diff --git a/core/res/res/values-w213dp/dimens_material.xml b/core/res/res/values-w213dp/dimens_material.xml
index 8a4e3a0..093c298 100644
--- a/core/res/res/values-w213dp/dimens_material.xml
+++ b/core/res/res/values-w213dp/dimens_material.xml
@@ -14,7 +14,11 @@
      limitations under the License.
 -->
 <resources>
+    <dimen name="screen_percentage_0416">8.85dp</dimen>
     <dimen name="screen_percentage_05">10.65dp</dimen>
+    <dimen name="screen_percentage_052">11.07dp</dimen>
     <dimen name="screen_percentage_10">21.3dp</dimen>
+    <dimen name="screen_percentage_12">25.56dp</dimen>
     <dimen name="screen_percentage_15">31.95dp</dimen>
+    <dimen name="screen_percentage_3646">77.66dp</dimen>
 </resources>
diff --git a/core/res/res/values-w216dp/dimens_material.xml b/core/res/res/values-w216dp/dimens_material.xml
new file mode 100644
index 0000000..71dbf72
--- /dev/null
+++ b/core/res/res/values-w216dp/dimens_material.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2024 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources>
+    <dimen name="screen_percentage_0416">8.99dp</dimen>
+    <dimen name="screen_percentage_05">10.8dp</dimen>
+    <dimen name="screen_percentage_052">11.23dp</dimen>
+    <dimen name="screen_percentage_10">21.6dp</dimen>
+    <dimen name="screen_percentage_12">25.92dp</dimen>
+    <dimen name="screen_percentage_15">32.4dp</dimen>
+    <dimen name="screen_percentage_3646">78.77dp</dimen>
+</resources>
diff --git a/core/res/res/values-w225dp/dimens_material.xml b/core/res/res/values-w225dp/dimens_material.xml
index aa822a3..6df34a5 100644
--- a/core/res/res/values-w225dp/dimens_material.xml
+++ b/core/res/res/values-w225dp/dimens_material.xml
@@ -14,7 +14,11 @@
      limitations under the License.
 -->
 <resources>
+    <dimen name="screen_percentage_0416">9.36dp</dimen>
     <dimen name="screen_percentage_05">11.25dp</dimen>
+    <dimen name="screen_percentage_052">11.7dp</dimen>
     <dimen name="screen_percentage_10">22.5dp</dimen>
+    <dimen name="screen_percentage_12">27dp</dimen>
     <dimen name="screen_percentage_15">33.75dp</dimen>
+    <dimen name="screen_percentage_3646">82.46dp</dimen>
 </resources>
diff --git a/core/res/res/values-w227dp/dimens_material.xml b/core/res/res/values-w227dp/dimens_material.xml
index eb4df8a2..bbf4924 100644
--- a/core/res/res/values-w227dp/dimens_material.xml
+++ b/core/res/res/values-w227dp/dimens_material.xml
@@ -14,7 +14,11 @@
      limitations under the License.
 -->
 <resources>
+    <dimen name="screen_percentage_0416">9.44dp</dimen>
     <dimen name="screen_percentage_05">11.35dp</dimen>
+    <dimen name="screen_percentage_052">11.8dp</dimen>
     <dimen name="screen_percentage_10">22.7dp</dimen>
+    <dimen name="screen_percentage_12">27.24dp</dimen>
     <dimen name="screen_percentage_15">34.05dp</dimen>
+    <dimen name="screen_percentage_3646">83.19dp</dimen>
 </resources>
diff --git a/core/res/res/values-w228dp/dimens_material.xml b/core/res/res/values-w228dp/dimens_material.xml
index a200975..24bbb4c 100644
--- a/core/res/res/values-w228dp/dimens_material.xml
+++ b/core/res/res/values-w228dp/dimens_material.xml
@@ -14,7 +14,11 @@
      limitations under the License.
 -->
 <resources>
+    <dimen name="screen_percentage_0416">9.48dp</dimen>
     <dimen name="screen_percentage_05">11.4dp</dimen>
+    <dimen name="screen_percentage_052">11.86dp</dimen>
     <dimen name="screen_percentage_10">22.8dp</dimen>
+    <dimen name="screen_percentage_12">27.36dp</dimen>
     <dimen name="screen_percentage_15">34.2dp</dimen>
+    <dimen name="screen_percentage_3646">83.55dp</dimen>
 </resources>
diff --git a/core/res/res/values-w240dp/dimens_material.xml b/core/res/res/values-w240dp/dimens_material.xml
index a4b58fa9..bd26c8b 100644
--- a/core/res/res/values-w240dp/dimens_material.xml
+++ b/core/res/res/values-w240dp/dimens_material.xml
@@ -14,7 +14,11 @@
      limitations under the License.
 -->
 <resources>
+    <dimen name="screen_percentage_0416">9.98dp</dimen>
     <dimen name="screen_percentage_05">12dp</dimen>
+    <dimen name="screen_percentage_052">12.48dp</dimen>
     <dimen name="screen_percentage_10">24dp</dimen>
+    <dimen name="screen_percentage_12">28.8dp</dimen>
     <dimen name="screen_percentage_15">36dp</dimen>
+    <dimen name="screen_percentage_3646">87.5dp</dimen>
 </resources>
diff --git a/core/res/res/values-watch-v36/colors.xml b/core/res/res/values-watch-v36/colors.xml
deleted file mode 100644
index 4bc2a66..0000000
--- a/core/res/res/values-watch-v36/colors.xml
+++ /dev/null
@@ -1,18 +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.
-  -->
-<!-- TODO(b/372524566): update color token's value to match material3 design. -->
-<resources>
-</resources>
\ No newline at end of file
diff --git a/core/res/res/values-watch-v36/dimens_material.xml b/core/res/res/values-watch-v36/dimens_material.xml
deleted file mode 100644
index 7232786..0000000
--- a/core/res/res/values-watch-v36/dimens_material.xml
+++ /dev/null
@@ -1,39 +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.
-  -->
-<resources>
-    <!-- values for material3 button -->
-    <dimen name="btn_material_width">172dp</dimen>
-    <dimen name="btn_material_height">52dp</dimen>
-    <dimen name="btn_horizontal_edge_padding">14dp</dimen>
-    <dimen name="btn_drawable_padding">6dp</dimen>
-    <dimen name="btn_lineHeight">18sp</dimen>
-    <dimen name="btn_textSize">15sp</dimen>
-
-    <!-- values for material3 AlertDialog -->
-    <dimen name="dialog_btn_negative_width">60dp</dimen>
-    <dimen name="dialog_btn_negative_height">60dp</dimen>
-    <dimen name="dialog_btn_confirm_width">62dp</dimen>
-    <dimen name="dialog_btn_confirm_height">60dp</dimen>
-
-    <!-- Opacity factor for disabled material3 widget -->
-    <dimen name="disabled_alpha_device_default">0.12</dimen>
-    <dimen name="primary_content_alpha_device_default">0.38</dimen>
-
-    <!--  values for material3 progress bar(progress indicator)  -->
-    <item name="progressbar_inner_radius_ratio" format="float" type="dimen">2.12</item>
-    <dimen name="progressbar_thickness">8dp</dimen>
-    <dimen name="progressbar_elevation">0.1dp</dimen>
-</resources>
diff --git a/core/res/res/values-watch-v36/styles_material.xml b/core/res/res/values-watch-v36/styles_material.xml
deleted file mode 100644
index 6e5ef68..0000000
--- a/core/res/res/values-watch-v36/styles_material.xml
+++ /dev/null
@@ -1,105 +0,0 @@
-<?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>
-    <!--  Button Styles  -->
-    <!-- Material Button - Filled (primary colored) -->
-    <style name="Widget.DeviceDefault.Button.Filled" parent="Widget.DeviceDefault.Button.WearMaterial3">
-        <item name="android:background">@drawable/btn_background_material_filled</item>
-        <item name="textAppearance">@style/TextAppearance.Widget.Button.Material.Filled</item>
-    </style>
-
-    <!-- Material Button - Filled Tonal (Override system default button styles) -->
-    <style name="Widget.DeviceDefault.Button.WearMaterial3">
-        <item name="background">@drawable/btn_background_material_filled_tonal</item>
-        <item name="textAppearance">@style/TextAppearance.Widget.Button.Material</item>
-        <item name="minHeight">@dimen/btn_material_height</item>
-        <item name="maxWidth">@dimen/btn_material_width</item>
-        <item name="android:paddingStart">@dimen/btn_horizontal_edge_padding</item>
-        <item name="android:paddingEnd">@dimen/btn_horizontal_edge_padding</item>
-        <item name="android:drawablePadding">@dimen/btn_drawable_padding</item>
-        <item name="android:maxLines">2</item>
-        <item name="android:ellipsize">end</item>
-        <item name="android:breakStrategy">simple</item>
-        <item name="stateListAnimator">@anim/button_state_list_anim_material</item>
-        <item name="focusable">true</item>
-        <item name="clickable">true</item>
-        <item name="gravity">center_vertical</item>
-    </style>
-
-    <!-- Material Button - Outlined -->
-    <style name="Widget.DeviceDefault.Button.Outlined" parent="Widget.DeviceDefault.Button.WearMaterial3">
-        <item name="android:background">@drawable/btn_background_material_outlined</item>
-    </style>
-
-    <!-- Material Button - Text -->
-    <style name="Widget.DeviceDefault.Button.Text" parent="Widget.DeviceDefault.Button.WearMaterial3">
-        <item name="android:background">@drawable/btn_background_material_text</item>
-    </style>
-
-    <!--  Text Styles  -->
-    <!-- TextAppearance for Material Button - Filled  -->
-    <style name="TextAppearance.Widget.Button.Material.Filled">
-        <item name="textColor">@color/btn_material_filled_content_color</item>
-    </style>
-
-    <!-- TextAppearance for Material Button - Filled Tonal  -->
-    <style name="TextAppearance.Widget.Button.Material" parent="TextAppearance.DeviceDefault">
-        <item name="android:fontFamily">font-family-flex-device-default</item>
-        <item name="android:fontVariationSettings">"'wdth' 90, 'wght' 500, 'ROND' 100, 'opsz' 15, 'GRAD' 0"</item>
-        <item name="textSize">@dimen/btn_textSize</item>
-        <item name="textColor">@color/btn_material_filled_tonal_content_color</item>
-        <item name="lineHeight">@dimen/btn_lineHeight</item>
-    </style>
-
-    <!--  AlertDialog Styles  -->
-    <style name="AlertDialog.DeviceDefault.WearMaterial3">
-        <item name="layout">@layout/alert_dialog_wear_material3</item>
-    </style>
-
-    <style name="Widget.DeviceDefault.Button.ButtonBar.AlertDialog.WearMaterial3" parent="Widget.DeviceDefault.Button">
-        <item name="android:textSize">0sp</item>
-        <item name="android:gravity">center</item>
-        <item name="android:paddingStart">0dp</item>
-        <item name="android:paddingEnd">0dp</item>
-        <item name="android:drawablePadding">0dp</item>
-    </style>
-
-    <style name="Widget.DeviceDefault.Button.ButtonBar.AlertDialog.WearMaterial3.Confirm">
-        <!-- Use a ImageView as background -->
-        <item name="background">@android:color/transparent</item>
-        <item name="minWidth">@dimen/dialog_btn_confirm_width</item>
-        <item name="minHeight">@dimen/dialog_btn_confirm_height</item>
-    </style>
-
-    <style name="Widget.DeviceDefault.Button.ButtonBar.AlertDialog.WearMaterial3.Negative">
-        <item name="background">@drawable/dialog_alert_button_negative</item>
-        <item name="minWidth">@dimen/dialog_btn_negative_width</item>
-        <item name="minHeight">@dimen/dialog_btn_negative_height</item>
-        <item name="maxWidth">@dimen/dialog_btn_negative_width</item>
-        <item name="maxHeight">@dimen/dialog_btn_negative_height</item>
-    </style>
-
-    <!-- Wear Material3 Progress Bar style: progressed ring.-->
-    <style name="Widget.DeviceDefault.ProgressBar.WearMaterial3">
-        <item name="indeterminateOnly">false</item>
-        <item name="progressDrawable">@drawable/progress_ring_wear_material3</item>
-        <item name="minHeight">@dimen/progress_bar_height</item>
-        <item name="maxHeight">@dimen/progress_bar_height</item>
-        <item name="mirrorForRtl">true</item>
-    </style>
-</resources>
\ No newline at end of file
diff --git a/core/res/res/values-watch/styles_device_default.xml b/core/res/res/values-watch/styles_device_default.xml
deleted file mode 100644
index 8a2ce5d..0000000
--- a/core/res/res/values-watch/styles_device_default.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2021 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<resources>
-    <style name="DialogWindowTitle.DeviceDefault" parent="DialogWindowTitle.Material">
-      <item name="maxLines">2</item>
-      <item name="shadowRadius">0</item>
-      <item name="ellipsize">end</item>
-      <item name="textAppearance">@style/TextAppearance.DeviceDefault.Title</item>
-    </style>
-    <style name="TextAppearance.DeviceDefault.Body1" parent="TextAppearance.Material.Body1">
-        <item name="android:textSize">15sp</item>
-        <item name="android:fontFamily">sans-serif</item>
-    </style>
-    <style name="TextAppearance.DeviceDefault.Title" parent="TextAppearance.Material.Title">
-        <item name="android:textSize">16sp</item>
-        <item name="android:fontFamily">google-sans</item>
-        <item name="android:textStyle">bold</item>
-    </style>
-    <style name="TextAppearance.DeviceDefault.Subhead" parent="TextAppearance.Material.Subhead">
-        <item name="android:textSize">16sp</item>
-        <item name="android:fontFamily">google-sans-medium</item>
-    </style>
-    <style name="BaseErrorDialog.DeviceDefault" parent="AlertDialog.DeviceDefault">
-        <item name="layout">@layout/watch_base_error_dialog</item>
-    </style>
-</resources>
diff --git a/core/res/res/values-watch/styles_device_defaults.xml b/core/res/res/values-watch/styles_device_defaults.xml
new file mode 100644
index 0000000..d8d424a
--- /dev/null
+++ b/core/res/res/values-watch/styles_device_defaults.xml
@@ -0,0 +1,95 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources>
+    <style name="DialogWindowTitle.DeviceDefault" parent="DialogWindowTitle.Material">
+        <item name="maxLines">2</item>
+        <item name="shadowRadius">0</item>
+        <item name="ellipsize">end</item>
+        <item name="textAppearance">@style/TextAppearance.DeviceDefault.Title</item>
+    </style>
+    <style name="TextAppearance.DeviceDefault.Body1" parent="TextAppearance.Material.Body1">
+        <item name="android:textSize">15sp</item>
+        <item name="android:fontFamily">sans-serif</item>
+    </style>
+    <style name="TextAppearance.DeviceDefault.Title" parent="TextAppearance.Material.Title">
+        <item name="android:textSize">16sp</item>
+        <item name="android:fontFamily">google-sans</item>
+        <item name="android:textStyle">bold</item>
+    </style>
+    <style name="TextAppearance.DeviceDefault.Subhead" parent="TextAppearance.Material.Subhead">
+        <item name="android:textSize">16sp</item>
+        <item name="android:fontFamily">google-sans-medium</item>
+    </style>
+    <style name="BaseErrorDialog.DeviceDefault" parent="AlertDialog.DeviceDefault">
+        <item name="layout">@layout/watch_base_error_dialog</item>
+    </style>
+
+    <!--  Button Styles  -->
+    <!-- Material Button - Filled (primary colored) -->
+    <style name="Widget.DeviceDefault.Button.Filled" parent="Widget.DeviceDefault.Button.WearMaterial3">
+        <item name="android:background">@drawable/btn_background_material_filled_watch</item>
+        <item name="textAppearance">@style/TextAppearance.Widget.Button.Material.Filled</item>
+    </style>
+
+    <!-- Material Button - Filled Tonal (Override system default button styles) -->
+    <style name="Widget.DeviceDefault.Button.WearMaterial3">
+        <item name="background">@drawable/btn_background_material_filled_tonal_watch</item>
+        <item name="textAppearance">@style/TextAppearance.Widget.Button.Material</item>
+        <item name="minHeight">@dimen/btn_material_height</item>
+        <item name="maxWidth">@dimen/btn_material_width</item>
+        <item name="android:paddingStart">@dimen/btn_horizontal_edge_padding</item>
+        <item name="android:paddingEnd">@dimen/btn_horizontal_edge_padding</item>
+        <item name="android:drawablePadding">@dimen/btn_drawable_padding</item>
+        <item name="android:maxLines">2</item>
+        <item name="android:ellipsize">end</item>
+        <item name="android:breakStrategy">simple</item>
+        <item name="stateListAnimator">@anim/button_state_list_anim_material</item>
+        <item name="focusable">true</item>
+        <item name="clickable">true</item>
+        <item name="gravity">center_vertical</item>
+    </style>
+
+    <!-- Wear Material3 Button - Outlined -->
+    <style name="Widget.DeviceDefault.Button.Outlined" parent="Widget.DeviceDefault.Button.WearMaterial3">
+        <item name="android:background">@drawable/btn_background_material_outlined_watch</item>
+    </style>
+
+    <!-- Wear Material3 Button - Text -->
+    <style name="Widget.DeviceDefault.Button.Text" parent="Widget.DeviceDefault.Button.WearMaterial3">
+        <item name="android:background">@drawable/btn_background_material_text_watch</item>
+    </style>
+
+    <!--  Wear Material3 AlertDialog Styles  -->
+    <style name="AlertDialog.DeviceDefault.WearMaterial3">
+        <item name="layout">@layout/alert_dialog_watch</item>
+    </style>
+
+    <!-- Wear Material3 Progress Bar style: progressed ring.-->
+    <style name="Widget.DeviceDefault.ProgressBar.WearMaterial3">
+        <item name="indeterminateOnly">false</item>
+        <item name="progressDrawable">@drawable/progress_ring_watch</item>
+        <item name="minHeight">@dimen/progress_bar_height</item>
+        <item name="maxHeight">@dimen/progress_bar_height</item>
+        <item name="mirrorForRtl">true</item>
+    </style>
+
+    <style name="Widget.DeviceDefault.ProgressBar" parent="Widget.Material.ProgressBar">
+        <!-- Allow determinate option -->
+        <item name="indeterminateOnly">false</item>
+        <!-- Use Wear Material3 ring shape as default determinate drawable -->
+        <item name="progressDrawable">@drawable/progress_ring_watch</item>
+    </style>
+</resources>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 14e8557..120c08c 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -1158,38 +1158,22 @@
     <string name="duration_hours_shortest_future" msgid="2979276794547981674">"<xliff:g id="COUNT">%d</xliff:g>小时后"</string>
     <string name="duration_days_shortest_future" msgid="3392722163935571543">"<xliff:g id="COUNT">%d</xliff:g>天后"</string>
     <string name="duration_years_shortest_future" msgid="5537464088352970388">"<xliff:g id="COUNT">%d</xliff:g>年后"</string>
-    <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
-    <skip />
-    <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
-    <skip />
-    <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
-    <skip />
-    <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium (1465359726485910115) -->
-    <skip />
-    <!-- no translation found for duration_days_medium (5994225628248661388) -->
-    <skip />
-    <!-- no translation found for duration_years_medium (734023884353592526) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_past (893797065424596243) -->
-    <skip />
+    <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g> 分钟前"</string>
+    <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g> 小时前"</string>
+    <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g> 天前"</string>
+    <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g> 年前"</string>
+    <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> 分钟"</string>
+    <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> 小时"</string>
+    <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> 天"</string>
+    <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> 年"</string>
+    <string name="duration_minutes_medium_future" msgid="2750894988731934402">"<xliff:g id="COUNT">%d</xliff:g> 分钟后"</string>
+    <string name="duration_hours_medium_future" msgid="6050833881463849764">"<xliff:g id="COUNT">%d</xliff:g> 小时后"</string>
+    <string name="duration_days_medium_future" msgid="1700821545602729963">"<xliff:g id="COUNT">%d</xliff:g> 天后"</string>
+    <string name="duration_years_medium_future" msgid="3281018940397120166">"<xliff:g id="COUNT">%d</xliff:g> 年后"</string>
+    <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g> 分钟前"</string>
+    <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g> 小时前"</string>
+    <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g> 天前"</string>
+    <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g> 年前"</string>
     <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# 分钟前}other{# 分钟前}}"</string>
     <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# 小时前}other{# 小时前}}"</string>
     <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# 天前}other{# 天前}}"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index c4079e0..9d6a232 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -1158,38 +1158,22 @@
     <string name="duration_hours_shortest_future" msgid="2979276794547981674">"<xliff:g id="COUNT">%d</xliff:g> 小時後"</string>
     <string name="duration_days_shortest_future" msgid="3392722163935571543">"<xliff:g id="COUNT">%d</xliff:g> 天後"</string>
     <string name="duration_years_shortest_future" msgid="5537464088352970388">"<xliff:g id="COUNT">%d</xliff:g> 年後"</string>
-    <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
-    <skip />
-    <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
-    <skip />
-    <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
-    <skip />
-    <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium (1465359726485910115) -->
-    <skip />
-    <!-- no translation found for duration_days_medium (5994225628248661388) -->
-    <skip />
-    <!-- no translation found for duration_years_medium (734023884353592526) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_past (893797065424596243) -->
-    <skip />
+    <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g> 分前"</string>
+    <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g> 小時前"</string>
+    <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g> 天前"</string>
+    <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g> 年前"</string>
+    <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> 分鐘"</string>
+    <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> 小時"</string>
+    <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> 天"</string>
+    <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> 年"</string>
+    <string name="duration_minutes_medium_future" msgid="2750894988731934402">"<xliff:g id="COUNT">%d</xliff:g> 分鐘後"</string>
+    <string name="duration_hours_medium_future" msgid="6050833881463849764">"<xliff:g id="COUNT">%d</xliff:g> 小時前"</string>
+    <string name="duration_days_medium_future" msgid="1700821545602729963">"<xliff:g id="COUNT">%d</xliff:g> 天後"</string>
+    <string name="duration_years_medium_future" msgid="3281018940397120166">"<xliff:g id="COUNT">%d</xliff:g> 年後"</string>
+    <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g> 分鐘前"</string>
+    <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g> 小時前"</string>
+    <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g> 天前"</string>
+    <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g> 年前"</string>
     <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# 分鐘前}other{# 分鐘前}}"</string>
     <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# 小時前}other{# 小時前}}"</string>
     <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# 天前}other{# 天前}}"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 3c3a0ef..7f22ab4 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -1158,38 +1158,22 @@
     <string name="duration_hours_shortest_future" msgid="2979276794547981674">"<xliff:g id="COUNT">%d</xliff:g> 小時後"</string>
     <string name="duration_days_shortest_future" msgid="3392722163935571543">"<xliff:g id="COUNT">%d</xliff:g> 天後"</string>
     <string name="duration_years_shortest_future" msgid="5537464088352970388">"<xliff:g id="COUNT">%d</xliff:g> 年後"</string>
-    <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
-    <skip />
-    <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
-    <skip />
-    <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
-    <skip />
-    <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium (1465359726485910115) -->
-    <skip />
-    <!-- no translation found for duration_days_medium (5994225628248661388) -->
-    <skip />
-    <!-- no translation found for duration_years_medium (734023884353592526) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_past (893797065424596243) -->
-    <skip />
+    <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g> 分鐘前"</string>
+    <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g> 小時前"</string>
+    <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g> 天前"</string>
+    <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g> 年前"</string>
+    <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> 分鐘"</string>
+    <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> 小時"</string>
+    <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> 天"</string>
+    <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> 年"</string>
+    <string name="duration_minutes_medium_future" msgid="2750894988731934402">"<xliff:g id="COUNT">%d</xliff:g> 分鐘後"</string>
+    <string name="duration_hours_medium_future" msgid="6050833881463849764">"<xliff:g id="COUNT">%d</xliff:g> 小時後"</string>
+    <string name="duration_days_medium_future" msgid="1700821545602729963">"<xliff:g id="COUNT">%d</xliff:g> 天後"</string>
+    <string name="duration_years_medium_future" msgid="3281018940397120166">"<xliff:g id="COUNT">%d</xliff:g> 年後"</string>
+    <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g> 分鐘前"</string>
+    <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g> 小時前"</string>
+    <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g> 天前"</string>
+    <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g> 年前"</string>
     <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# 分鐘前}other{# 分鐘前}}"</string>
     <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# 小時前}other{# 小時前}}"</string>
     <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# 天前}other{# 天前}}"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index dc72cb7..0ac3e4a 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -1158,38 +1158,22 @@
     <string name="duration_hours_shortest_future" msgid="2979276794547981674">"ngehora elingu-<xliff:g id="COUNT">%d</xliff:g>"</string>
     <string name="duration_days_shortest_future" msgid="3392722163935571543">"ngosuku olu-<xliff:g id="COUNT">%d</xliff:g>"</string>
     <string name="duration_years_shortest_future" msgid="5537464088352970388">"ngonyaka ongu-<xliff:g id="COUNT">%d</xliff:g>"</string>
-    <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
-    <skip />
-    <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
-    <skip />
-    <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
-    <skip />
-    <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium (1465359726485910115) -->
-    <skip />
-    <!-- no translation found for duration_days_medium (5994225628248661388) -->
-    <skip />
-    <!-- no translation found for duration_years_medium (734023884353592526) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
-    <skip />
-    <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
-    <skip />
-    <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
-    <skip />
-    <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
-    <skip />
-    <!-- no translation found for duration_years_medium_past (893797065424596243) -->
-    <skip />
+    <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"Umzuzu ongu-<xliff:g id="COUNT">%d</xliff:g> odlule"</string>
+    <string name="duration_hours_shortest_past" msgid="2098397414186628489">"Ihora eli-<xliff:g id="COUNT">%d</xliff:g> eledlule"</string>
+    <string name="duration_days_shortest_past" msgid="1832006037955897625">"Usuku olu-<xliff:g id="COUNT">%d</xliff:g> oludlule"</string>
+    <string name="duration_years_shortest_past" msgid="6168256514200469291">"Unyaka ongu-<xliff:g id="COUNT">%d</xliff:g> odlule"</string>
+    <string name="duration_minutes_medium" msgid="5891933490342643944">"Umzuzu o-<xliff:g id="COUNT">%d</xliff:g>"</string>
+    <string name="duration_hours_medium" msgid="1465359726485910115">"Ihora eli-<xliff:g id="COUNT">%d</xliff:g>"</string>
+    <string name="duration_days_medium" msgid="5994225628248661388">"Usuku olu-<xliff:g id="COUNT">%d</xliff:g>"</string>
+    <string name="duration_years_medium" msgid="734023884353592526">"Unyaka ongu-<xliff:g id="COUNT">%d</xliff:g>"</string>
+    <string name="duration_minutes_medium_future" msgid="2750894988731934402">"Emzuzwini ongu-<xliff:g id="COUNT">%d</xliff:g>"</string>
+    <string name="duration_hours_medium_future" msgid="6050833881463849764">"Emahoreni angu-<xliff:g id="COUNT">%d</xliff:g>"</string>
+    <string name="duration_days_medium_future" msgid="1700821545602729963">"Osukwini olu-<xliff:g id="COUNT">%d</xliff:g>"</string>
+    <string name="duration_years_medium_future" msgid="3281018940397120166">"onyakeni ongu-<xliff:g id="COUNT">%d</xliff:g>"</string>
+    <string name="duration_minutes_medium_past" msgid="7400424340181947714">"Umzuzu ongu-<xliff:g id="COUNT">%d</xliff:g> odlule"</string>
+    <string name="duration_hours_medium_past" msgid="6709441336035202617">"Ihora eli-<xliff:g id="COUNT">%d</xliff:g> eledlule"</string>
+    <string name="duration_days_medium_past" msgid="5748156261134344532">"Usuku olu-<xliff:g id="COUNT">%d</xliff:g> oludlule"</string>
+    <string name="duration_years_medium_past" msgid="893797065424596243">"Unyaka ongu-<xliff:g id="COUNT">%d</xliff:g> odlule"</string>
     <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{umzuzu odlule #}one{imizuzu edlule #}other{imizuzu edlule #}}"</string>
     <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{ihora elingu-# eledlule}one{amahora adlule angu-#}other{amahora adlule angu-#}}"</string>
     <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{usuku oludlule #}one{izinsuku ezedlule #}other{izinsuku ezedlule #}}"</string>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 792974d..728c856 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -4641,7 +4641,7 @@
              role owner must opt into this behavior by using the property named by
              {@link android.nfc.cardemulation.CardEmulation.PROPERTY_ALLOW_SHARED_ROLE_PRIORITY }
              in the <code>&lt;application&rt;</code> tag. -->
-        <attr name="shareRolePriority" format="boolean"/>
+        <attr name="wantsRoleHolderPriority" format="boolean"/>
     </declare-styleable>
 
     <!-- Use <code>offhost-apdu-service</code> as the root tag of the XML resource that
@@ -4669,7 +4669,7 @@
         <!-- Whether the device should default to observe mode when this service is
              default or in the foreground. -->
         <attr name="shouldDefaultToObserveMode"/>
-        <attr name="shareRolePriority"/>
+        <attr name="wantsRoleHolderPriority"/>
     </declare-styleable>
 
     <!-- Specify one or more <code>aid-group</code> elements inside a
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 00c59c6..8c6fd1d 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -2572,7 +2572,9 @@
              against a development branch, in which case it will only work against
              the development builds. -->
         <attr name="minSdkVersion" format="integer|string" />
-        <!-- @FlaggedApi(android.sdk.Flags.FLAG_MAJOR_MINOR_VERSIONING_SCHEME) -->
+        <!-- This is the minimum SDK major and minor version (e.g. "36.1") that
+             the application requires. Verified independently of minSdkVersion.
+             @FlaggedApi(android.sdk.Flags.FLAG_MAJOR_MINOR_VERSIONING_SCHEME) -->
         <attr name="minSdkVersionFull" format="string" />
         <!-- This is the SDK version number that the application is targeting.
              It is able to run on older versions (down to minSdkVersion), but
@@ -2890,6 +2892,17 @@
         <attr name="name" />
     </declare-styleable>
 
+    <!-- Private tag to declare the package name that the permissions of this package
+         is based on.  Only used for packages installed in the system image.  If
+         given, the permissions from the other package will be propagated into the
+         new package.
+
+         <p>This appears as a child tag of the root
+         {@link #AndroidManifest manifest} tag. -->
+    <declare-styleable name="AndroidManifestAdoptPermissions" parent="AndroidManifest">
+        <attr name="name" />
+    </declare-styleable>
+
     <!-- The <code>processes</code> tag specifies the processes the application will run code in
          and optionally characteristics of those processes.  This tag is optional; if not
          specified, components will simply run in the processes they specify.  If supplied,
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 53b47622..565e28e 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1607,6 +1607,10 @@
          brightness value and will repeat for the following ramp if autobrightness is enabled. -->
     <bool name="config_skipScreenOnBrightnessRamp">false</bool>
 
+    <!-- Whether or not to skip a color fade transition to black when the display transitions to
+         STATE_OFF. Setting this to true will skip the color fade transition. -->
+    <bool name="config_skipScreenOffTransition">false</bool>
+
     <!-- Allow automatic adjusting of the screen brightness while dozing in low power state. -->
     <bool name="config_allowAutoBrightnessWhileDozing">false</bool>
 
@@ -2466,9 +2470,6 @@
     <string name="config_systemCallStreaming" translatable="false"></string>
     <!-- The name of the package that will hold the default retail demo role. -->
     <string name="config_defaultRetailDemo" translatable="false"></string>
-    <!-- The name of the package that will hold the default reserved for testing profile group
-         exclusivity role. -->
-    <string name="config_defaultReservedForTestingProfileGroupExclusivity" translatable="false">android.app.rolemultiuser.cts.app</string>
 
     <!-- The component name of the wear service class that will be started by the system server. -->
     <string name="config_wearServiceComponent" translatable="false"></string>
@@ -7212,10 +7213,6 @@
          screen. -->
     <bool name="config_dragToMaximizeInDesktopMode">false</bool>
 
-    <!-- Whether showing the app handle is supported on this device.
-         If config_isDesktopModeSupported, then this has no effect -->
-    <bool name="config_enableAppHandle">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>
@@ -7280,4 +7277,7 @@
          features. Examples include the search functionality or the app
          predictor. -->
     <string name="config_systemVendorIntelligence" translatable="false"></string>
+
+    <!-- Whether the device supports Wi-Fi USD feature. -->
+    <bool name="config_deviceSupportsWifiUsd">false</bool>
 </resources>
diff --git a/core/res/res/values/config_battery_stats.xml b/core/res/res/values/config_battery_stats.xml
index 9498273..1b45373 100644
--- a/core/res/res/values/config_battery_stats.xml
+++ b/core/res/res/values/config_battery_stats.xml
@@ -53,4 +53,6 @@
     battery history, in bytes. -->
     <integer name="config_accumulatedBatteryUsageStatsSpanSize">32768</integer>
 
+    <!-- Size of storage allocated to battery history, in bytes -->
+    <integer name="config_batteryHistoryStorageSize">4194304</integer>
 </resources>
diff --git a/core/res/res/values/config_telephony.xml b/core/res/res/values/config_telephony.xml
index 196da29..666f1cf 100644
--- a/core/res/res/values/config_telephony.xml
+++ b/core/res/res/values/config_telephony.xml
@@ -471,6 +471,12 @@
     <string name="satellite_access_config_file" translatable="false"></string>
     <java-symbol type="string" name="satellite_access_config_file" />
 
+    <!-- A string defines the NIDD (Non-IP Data Delivery) APN to be used for satellite attachment. For more on NIDD,
+         see 3GPP TS 29.542. This config is used for an NTN-only subscription, which requires activation before being used.
+         -->
+    <string name="config_satellite_nidd_apn_name" translatable="false"></string>
+    <java-symbol type="string" name="config_satellite_nidd_apn_name" />
+
     <!-- Boolean indicating whether to enable MT SMS polling for NB IOT NTN. -->
     <bool name="config_enabled_mt_sms_polling">true</bool>
     <java-symbol type="bool" name="config_enabled_mt_sms_polling" />
@@ -500,4 +506,10 @@
     <!-- Whether to allow TN scanning during satellite session. -->
     <bool name="config_satellite_allow_tn_scanning_during_satellite_session">true</bool>
     <java-symbol type="bool" name="config_satellite_allow_tn_scanning_during_satellite_session" />
+
+    <!-- List of integer tag Ids representing VZW satellite coverage. -->
+    <integer-array name="config_verizon_satellite_enabled_tagids">
+    </integer-array>
+    <java-symbol type="array" name="config_verizon_satellite_enabled_tagids" />
+
 </resources>
diff --git a/core/res/res/values-watch-v36/config.xml b/core/res/res/values/config_watch.xml
similarity index 88%
rename from core/res/res/values-watch-v36/config.xml
rename to core/res/res/values/config_watch.xml
index 679dc70..629a343 100644
--- a/core/res/res/values-watch-v36/config.xml
+++ b/core/res/res/values/config_watch.xml
@@ -15,6 +15,8 @@
   -->
 
 <resources>
+    <!--  TODO(b/382103556): use predefined Material3 token  -->
+    <!-- For Wear Material3 -->
     <dimen name="config_wearMaterial3_buttonCornerRadius">26dp</dimen>
     <dimen name="config_wearMaterial3_bottomDialogCornerRadius">18dp</dimen>
 </resources>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 51bd4cc..fb21c75 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -323,7 +323,7 @@
     <dimen name="notification_progress_margin_top">8dp</dimen>
 
     <!-- The horizontal margin before and after the notification progress bar. -->
-    <dimen name="notification_progress_margin_horizontal">2dp</dimen>
+    <dimen name="notification_progress_margin_horizontal">4dp</dimen>
 
     <!-- height of the notification header -->
     <dimen name="notification_header_height">56dp</dimen>
@@ -861,7 +861,7 @@
     <!-- The size of the progress tracker height -->
     <dimen name="notification_progress_tracker_height">20dp</dimen>
     <!-- The gap between segments in the notification progress bar -->
-    <dimen name="notification_progress_segSeg_gap">2dp</dimen>
+    <dimen name="notification_progress_segSeg_gap">4dp</dimen>
     <!-- The gap between a segment and a point in the notification progress bar -->
     <dimen name="notification_progress_segPoint_gap">4dp</dimen>
     <!-- The height of the notification progress bar segments -->
diff --git a/core/res/res/values/dimens_watch.xml b/core/res/res/values/dimens_watch.xml
new file mode 100644
index 0000000..2aae987
--- /dev/null
+++ b/core/res/res/values/dimens_watch.xml
@@ -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.
+  -->
+
+<resources>
+    <!-- values for wear material3 button -->
+    <dimen name="btn_material_width">172dp</dimen>
+    <dimen name="btn_material_height">52dp</dimen>
+    <dimen name="btn_horizontal_edge_padding">14dp</dimen>
+    <dimen name="btn_drawable_padding">6dp</dimen>
+    <dimen name="btn_lineHeight">18sp</dimen>
+    <dimen name="btn_textSize">15sp</dimen>
+
+    <!-- values for wear material3 AlertDialog Title -->
+    <dimen name="alertDialog_material_line_height_title">18sp</dimen>
+    <dimen name="alertDialog_material_text_size_title">16sp</dimen>
+    <item name="alertDialog_material_letter_spacing_title" format="float" type="dimen">0.0125</item>
+    <dimen name="alertDialog_material_side_margin_title">@dimen/screen_percentage_12</dimen>
+
+    <!-- values for wear material3 AlertDialog Body -->
+    <dimen name="alertDialog_material_line_height_body_1">16sp</dimen>
+    <dimen name="alertDialog_material_text_size_body_1">14sp</dimen>
+    <item name="alertDialog_material_letter_spacing_body_1" format="float" type="dimen">0.0286</item>
+    <dimen name="alertDialog_material_side_margin_body">@dimen/screen_percentage_0416</dimen>
+
+    <!-- values for wear material3 AlertDialog -->
+    <dimen name="dialog_btn_negative_width">60dp</dimen>
+    <dimen name="dialog_btn_negative_height">60dp</dimen>
+    <dimen name="dialog_btn_confirm_width">62dp</dimen>
+    <dimen name="dialog_btn_confirm_height">60dp</dimen>
+    <dimen name="alertDialog_material_side_margin">@dimen/screen_percentage_052</dimen>
+    <dimen name="alertDialog_material_top_margin">@dimen/screen_percentage_10</dimen>
+    <dimen name="alertDialog_material_bottom_margin">@dimen/screen_percentage_3646</dimen>
+
+    <!-- Opacity factor for disabled wear material3 widget -->
+    <!-- Alpha transparency for widgets that set enablement/disablement programmatically
+       transparency is applied in the disabled state -->
+    <dimen name="disabled_alpha_device_default">0.12</dimen>
+    <!-- Alpha transparency applied to elements which are considered primary (e.g. primary text) -->
+    <dimen name="primary_content_alpha_device_default">0.38</dimen>
+
+    <!--  values for wear material3 progress bar(progress indicator)  -->
+    <item name="progressbar_inner_radius_ratio" format="float" type="dimen">2.12</item>
+    <dimen name="progressbar_thickness">8dp</dimen>
+    <dimen name="progressbar_elevation">0.1dp</dimen>
+
+    <!-- Alpha transparency for wigets that set enablement/disablement programmatically
+     transparency is applied in the disabled state -->
+    <dimen name="disabled_alpha_wear_material3">0.12</dimen>
+    <!-- Alpha transparency applied to elements which are considered primary (e.g. primary text) -->
+    <dimen name="primary_content_alpha_wear_material3">0.38</dimen>
+</resources>
diff --git a/core/res/res/values/public-staging.xml b/core/res/res/values/public-staging.xml
index 6c73b0c..e82992b 100644
--- a/core/res/res/values/public-staging.xml
+++ b/core/res/res/values/public-staging.xml
@@ -134,7 +134,7 @@
     <!-- @FlaggedApi(android.content.pm.Flags.FLAG_APP_COMPAT_OPTION_16KB) -->
     <public name="pageSizeCompat" />
     <!-- @FlaggedApi(android.nfc.Flags.FLAG_NFC_ASSOCIATED_ROLE_SERVICES) -->
-    <public name="shareRolePriority"/>
+    <public name="wantsRoleHolderPriority"/>
     <!-- @FlaggedApi(android.sdk.Flags.FLAG_MAJOR_MINOR_VERSIONING_SCHEME) -->
     <public name="minSdkVersionFull"/>
   </staging-public-group>
@@ -151,12 +151,24 @@
     <!-- @FlaggedApi(android.content.pm.Flags.FLAG_SDK_DEPENDENCY_INSTALLER)
          @hide @SystemApi -->
     <public name="config_systemDependencyInstaller" />
-    <!-- @FlaggedApi(android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_PLATFORM_API_ENABLED)
-         @hide @SystemApi -->
-    <public name="config_defaultReservedForTestingProfileGroupExclusivity" />
+    <!-- @hide @SystemApi -->
+    <public name="removed_config_defaultReservedForTestingProfileGroupExclusivity" />
     <!-- @FlaggedApi(android.permission.flags.Flags.FLAG_SYSTEM_VENDOR_INTELLIGENCE_ROLE_ENABLED)
          @hide @SystemApi -->
     <public name="config_systemVendorIntelligence" />
+
+    <!-- @FlaggedApi(android.app.ondeviceintelligence.flags.Flags.FLAG_ENABLE_ON_DEVICE_INTELLIGENCE_MODULE)
+     @hide @SystemApi -->
+    <public name="config_defaultOnDeviceIntelligenceService"></public>
+
+    <!-- @FlaggedApi(android.app.ondeviceintelligence.flags.Flags.FLAG_ENABLE_ON_DEVICE_INTELLIGENCE_MODULE)
+     @hide @SystemApi -->
+    <public name="config_defaultOnDeviceSandboxedInferenceService"></public>
+
+    <!-- @FlaggedApi(android.app.ondeviceintelligence.flags.Flags.FLAG_ENABLE_ON_DEVICE_INTELLIGENCE_MODULE)
+     @hide @SystemApi -->
+    <public name="config_defaultOnDeviceIntelligenceDeviceConfigNamespace"></public>
+
   </staging-public-group>
 
   <staging-public-group type="dimen" first-id="0x01b30000">
diff --git a/core/res/res/values/styles_device_defaults.xml b/core/res/res/values/styles_device_defaults.xml
index acc1ff8..326afba 100644
--- a/core/res/res/values/styles_device_defaults.xml
+++ b/core/res/res/values/styles_device_defaults.xml
@@ -44,6 +44,10 @@
         <item name="textColor">@color/btn_colored_text_material</item>
     </style>
     <style name="Widget.DeviceDefault.Button.WearMaterial3"/>
+    <style name="Widget.DeviceDefault.Button.WearMaterial3.Filled"/>
+    <style name="Widget.DeviceDefault.Button.WearMaterial3.FilledTonal"/>
+    <style name="Widget.DeviceDefault.Button.WearMaterial3.Outlined"/>
+    <style name="Widget.DeviceDefault.Button.WearMaterial3.Text"/>
     <style name="Widget.DeviceDefault.TextView" parent="Widget.Material.TextView" />
     <style name="Widget.DeviceDefault.CheckedTextView" parent="Widget.Material.CheckedTextView"/>
     <style name="Widget.DeviceDefault.AutoCompleteTextView" parent="Widget.Material.AutoCompleteTextView"/>
@@ -60,6 +64,7 @@
     <style name="Widget.DeviceDefault.ProgressBar.Small" parent="Widget.Material.ProgressBar.Small"/>
     <style name="Widget.DeviceDefault.ProgressBar.Small.Title" parent="Widget.Material.ProgressBar.Small.Title"/>
     <style name="Widget.DeviceDefault.ProgressBar.Large" parent="Widget.Material.ProgressBar.Large"/>
+    <style name="Widget.DeviceDefault.ProgressBar.WearMaterial3"/>
     <style name="Widget.DeviceDefault.SeekBar" parent="Widget.Material.SeekBar"/>
     <style name="Widget.DeviceDefault.RatingBar" parent="Widget.Material.RatingBar"/>
     <style name="Widget.DeviceDefault.RatingBar.Indicator" parent="Widget.Material.RatingBar.Indicator"/>
@@ -430,6 +435,7 @@
     <!-- AlertDialog Styles -->
     <style name="AlertDialog.DeviceDefault" parent="AlertDialog.Material"/>
     <style name="AlertDialog.DeviceDefault.Light" parent="AlertDialog.Material.Light"/>
+    <style name="AlertDialog.DeviceDefault.WearMaterial3"/>
 
     <!-- Animation Styles -->
     <style name="Animation.DeviceDefault.Activity" parent="Animation.Material.Activity"/>
diff --git a/core/res/res/values/styles_watch.xml b/core/res/res/values/styles_watch.xml
new file mode 100644
index 0000000..3cf1f9b
--- /dev/null
+++ b/core/res/res/values/styles_watch.xml
@@ -0,0 +1,73 @@
+<!--
+  ~ Copyright (C) 2024 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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>
+    <!-- TextAppearance for Wear Material3 Button - Filled  -->
+    <style name="TextAppearance.Widget.Button.Material.Filled">
+        <item name="textColor">@color/btn_material_filled_content_color_watch</item>
+    </style>
+
+    <!-- TextAppearance for Wear Material3 Button - Filled Tonal  -->
+    <style name="TextAppearance.Widget.Button.Material" parent="TextAppearance.DeviceDefault">
+        <item name="android:fontFamily">font-family-flex-device-default</item>
+        <item name="android:fontVariationSettings">"'wdth' 90, 'wght' 500, 'ROND' 100, 'opsz' 15, 'GRAD' 0"</item>
+        <item name="textSize">@dimen/btn_textSize</item>
+        <item name="textColor">@color/btn_material_filled_tonal_content_color_watch</item>
+        <item name="lineHeight">@dimen/btn_lineHeight</item>
+    </style>
+
+    <style name="Widget.DeviceDefault.Button.ButtonBar.AlertDialog.WearMaterial3" parent="Widget.DeviceDefault.Button">
+        <item name="android:textSize">0sp</item>
+        <item name="android:gravity">center</item>
+        <item name="android:paddingStart">0dp</item>
+        <item name="android:paddingEnd">0dp</item>
+        <item name="android:drawablePadding">0dp</item>
+    </style>
+    <style name="Widget.DeviceDefault.Button.ButtonBar.AlertDialog.WearMaterial3.Confirm">
+        <!-- Use a ImageView as background -->
+        <item name="background">@android:color/transparent</item>
+        <item name="minWidth">@dimen/dialog_btn_confirm_width</item>
+        <item name="minHeight">@dimen/dialog_btn_confirm_height</item>
+    </style>
+    <style name="Widget.DeviceDefault.Button.ButtonBar.AlertDialog.WearMaterial3.Negative">
+        <item name="background">@drawable/dialog_alert_button_negative_watch</item>
+        <item name="minWidth">@dimen/dialog_btn_negative_width</item>
+        <item name="minHeight">@dimen/dialog_btn_negative_height</item>
+        <item name="maxWidth">@dimen/dialog_btn_negative_width</item>
+        <item name="maxHeight">@dimen/dialog_btn_negative_height</item>
+    </style>
+
+    <!-- TextAppearance for wear material3 AlertDialog Body  -->
+    <style name="TextAppearance.AlertDialog.Body1" parent="TextAppearance.Material.Body1">
+        <item name="android:fontFamily">font-family-flex-device-default</item>
+        <item name="android:fontVariationSettings">"'wdth' 90, 'wght' 450, 'ROND' 100, 'GRAD' 0"</item>
+        <item name="android:textSize">@dimen/alertDialog_material_text_size_body_1</item>
+        <item name="android:lineHeight">@dimen/alertDialog_material_line_height_body_1</item>
+        <item name="android:letterSpacing">@dimen/alertDialog_material_letter_spacing_body_1</item>
+    </style>
+
+    <!-- TextAppearance for wear material3 AlertDialog Title  -->
+    <style name="TextAppearance.AlertDialog.Title" parent="TextAppearance.Material.Title">
+        <item name="android:fontFamily">font-family-flex-device-default</item>
+        <item name="android:fontVariationSettings">"'wdth' 100, 'wght' 550, 'ROND' 100, 'GRAD' 0"</item>
+        <item name="android:textSize">@dimen/alertDialog_material_text_size_title</item>
+        <item name="android:lineHeight">@dimen/alertDialog_material_line_height_title</item>
+        <item name="android:letterSpacing">@dimen/alertDialog_material_letter_spacing_title</item>
+        <item name="android:maxLines">2</item>
+        <item name="android:shadowRadius">0</item>
+        <item name="android:ellipsize">end</item>
+    </style>
+</resources>
\ No newline at end of file
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 71532bb..73e06f6 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2045,6 +2045,7 @@
   <java-symbol type="bool" name="config_unplugTurnsOnScreen" />
   <java-symbol type="bool" name="config_usbChargingMessage" />
   <java-symbol type="bool" name="config_skipScreenOnBrightnessRamp" />
+  <java-symbol type="bool" name="config_skipScreenOffTransition" />
   <java-symbol type="bool" name="config_allowAutoBrightnessWhileDozing" />
   <java-symbol type="bool" name="config_allowTheaterModeWakeFromUnplug" />
   <java-symbol type="bool" name="config_allowTheaterModeWakeFromGesture" />
@@ -5358,6 +5359,7 @@
   <java-symbol type="integer" name="config_powerStatsAggregationPeriod" />
   <java-symbol type="integer" name="config_aggregatedPowerStatsSpanDuration" />
   <java-symbol type="integer" name="config_accumulatedBatteryUsageStatsSpanSize" />
+  <java-symbol type="integer" name="config_batteryHistoryStorageSize" />
 
   <!--Dynamic Tokens-->
   <java-symbol name="materialColorBackground" type="color"/>
@@ -5681,9 +5683,6 @@
        screen. -->
   <java-symbol type="bool" name="config_dragToMaximizeInDesktopMode" />
 
-  <!-- Whether showing the app handle is supported on this device -->
-  <java-symbol type="bool" name="config_enableAppHandle" />
-
   <!-- Frame rate compatibility value for Wallpaper -->
   <java-symbol type="integer" name="config_wallpaperFrameRateCompatibility" />
 
@@ -5802,5 +5801,7 @@
   <java-symbol type="dimen" name="config_shapeCornerRadiusLarge"/>
   <java-symbol type="dimen" name="config_shapeCornerRadiusXlarge"/>
 
+  <!-- Whether the device supports Wi-Fi USD feature. -->
+  <java-symbol type="bool" name="config_deviceSupportsWifiUsd" />
 
 </resources>
diff --git a/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java b/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
index bd27337..e9dfdd8 100644
--- a/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
+++ b/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
@@ -16,7 +16,6 @@
 
 package android.app;
 
-import static android.app.Flags.FLAG_PIC_CACHE_NULLS;
 import static android.app.Flags.FLAG_PIC_ISOLATE_CACHE_BY_UID;
 import static android.app.PropertyInvalidatedCache.NONCE_UNSET;
 import static android.app.PropertyInvalidatedCache.MODULE_BLUETOOTH;
@@ -711,7 +710,6 @@
         }
     }
 
-    @RequiresFlagsEnabled(FLAG_PIC_CACHE_NULLS)
     @Test
     public void testCachingNulls() {
         TestCache cache = new TestCache(new Args(MODULE_TEST)
diff --git a/core/tests/coretests/src/android/database/sqlite/SQLiteRawStatementTest.java b/core/tests/coretests/src/android/database/sqlite/SQLiteRawStatementTest.java
index 13b12fc..51a43ac0 100644
--- a/core/tests/coretests/src/android/database/sqlite/SQLiteRawStatementTest.java
+++ b/core/tests/coretests/src/android/database/sqlite/SQLiteRawStatementTest.java
@@ -1048,5 +1048,28 @@
         } finally {
             mDatabase.endTransaction();
         }
+
+        // Ensure that column names and column types can be fetched even if the statement is not
+        // stepped.  A new SQL statement is created to avoid interaction from the statement cache.
+        mDatabase.beginTransactionReadOnly();
+        try (SQLiteRawStatement s = mDatabase.createRawStatement("SELECT * from t1 WHERE j = 3")) {
+            // Do not step the statement.
+            assertEquals("i", s.getColumnName(0));
+            assertEquals("j", s.getColumnName(1));
+        } finally {
+            mDatabase.endTransaction();
+        }
+
+        mDatabase.beginTransactionReadOnly();
+        try (SQLiteRawStatement s = mDatabase.createRawStatement("SELECT * from t1")) {
+            // Do not step the statement.
+            s.getColumnName(3); // out-of-range column
+            fail("JNI exception not thrown");
+        } catch (SQLiteBindOrColumnIndexOutOfRangeException e) {
+            // Passing case.
+        } finally {
+            mDatabase.endTransaction();
+        }
+
     }
 }
diff --git a/core/tests/coretests/src/android/os/BuildTest.java b/core/tests/coretests/src/android/os/BuildTest.java
index 2a67716a..0bbfb65 100644
--- a/core/tests/coretests/src/android/os/BuildTest.java
+++ b/core/tests/coretests/src/android/os/BuildTest.java
@@ -178,6 +178,20 @@
     }
 
     @Test
+    public void testParseFullVersionIncorrectInputMajorVersionTooLarge() throws Exception {
+        assertThrows(IllegalArgumentException.class, () -> {
+            Build.parseFullVersion("40000.1");
+        });
+    }
+
+    @Test
+    public void testParseFullVersionIncorrectInputMinorVersionTooLarge() throws Exception {
+        assertThrows(IllegalArgumentException.class, () -> {
+            Build.parseFullVersion("3.99999999");
+        });
+    }
+
+    @Test
     public void testFullVersionToStringCorrectInput() throws Exception {
         assertEquals("0.0", Build.fullVersionToString(0));
         assertEquals("1.0", Build.fullVersionToString(1 * 100000 + 0));
diff --git a/core/tests/coretests/src/android/os/IpcDataCacheTest.java b/core/tests/coretests/src/android/os/IpcDataCacheTest.java
index bb8356f..fc04e64 100644
--- a/core/tests/coretests/src/android/os/IpcDataCacheTest.java
+++ b/core/tests/coretests/src/android/os/IpcDataCacheTest.java
@@ -16,7 +16,6 @@
 
 package android.os;
 
-import static android.app.Flags.FLAG_PIC_CACHE_NULLS;
 import static android.app.Flags.FLAG_PIC_ISOLATE_CACHE_BY_UID;
 
 import static org.junit.Assert.assertEquals;
@@ -511,7 +510,6 @@
         IpcDataCache.setTestMode(true);
     }
 
-    @RequiresFlagsEnabled(FLAG_PIC_CACHE_NULLS)
     @Test
     public void testCachingNulls() {
         IpcDataCache.Config c =
diff --git a/core/tests/vibrator/src/android/os/vibrator/persistence/VibrationEffectXmlSerializationTest.java b/core/tests/vibrator/src/android/os/vibrator/persistence/VibrationEffectXmlSerializationTest.java
index 5f25e93..c058885 100644
--- a/core/tests/vibrator/src/android/os/vibrator/persistence/VibrationEffectXmlSerializationTest.java
+++ b/core/tests/vibrator/src/android/os/vibrator/persistence/VibrationEffectXmlSerializationTest.java
@@ -931,6 +931,545 @@
     }
 
     @Test
+    @EnableFlags(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
+    public void testRepeating_withWaveformEnvelopeEffect_allSucceed() throws Exception {
+        VibrationEffect preamble = new VibrationEffect.WaveformEnvelopeBuilder()
+                .addControlPoint(0.1f, 50f, 10)
+                .addControlPoint(0.2f, 60f, 20)
+                .build();
+        VibrationEffect repeating = new VibrationEffect.WaveformEnvelopeBuilder()
+                .setInitialFrequencyHz(70f)
+                .addControlPoint(0.3f, 80f, 25)
+                .addControlPoint(0.4f, 90f, 30)
+                .build();
+        VibrationEffect effect = VibrationEffect.createRepeatingEffect(preamble, repeating);
+
+        String xml = """
+                <vibration-effect>
+                <repeating-effect>
+                    <preamble>
+                        <waveform-envelope-effect>
+                            <control-point amplitude="0.1" frequencyHz="50.0" durationMs="10"/>
+                            <control-point amplitude="0.2" frequencyHz="60.0" durationMs="20"/>
+                        </waveform-envelope-effect>
+                    </preamble>
+                    <repeating>
+                        <waveform-envelope-effect initialFrequencyHz="70.0">
+                            <control-point amplitude="0.3" frequencyHz="80.0" durationMs="25"/>
+                            <control-point amplitude="0.4" frequencyHz="90.0" durationMs="30"/>
+                        </waveform-envelope-effect>
+                    </repeating>
+                </repeating-effect>
+                </vibration-effect>
+                """;
+
+        assertPublicApisParserSucceeds(xml, effect);
+        assertPublicApisSerializerSucceeds(effect, "0.1", "0.2", "0.3", "0.4", "50.0", "60.0",
+                "70.0", "80.0", "90.0", "10", "20", "25", "30");
+        assertPublicApisRoundTrip(effect);
+
+        assertHiddenApisParserSucceeds(xml, effect);
+        assertHiddenApisSerializerSucceeds(effect, "0.1", "0.2", "0.3", "0.4", "50.0", "60.0",
+                "70.0", "80.0", "90.0", "10", "20", "25", "30");
+        assertHiddenApisRoundTrip(effect);
+
+        effect = VibrationEffect.createRepeatingEffect(repeating);
+
+        xml = """
+                <vibration-effect>
+                <repeating-effect>
+                    <repeating>
+                        <waveform-envelope-effect initialFrequencyHz="70.0">
+                            <control-point amplitude="0.3" frequencyHz="80.0" durationMs="25"/>
+                            <control-point amplitude="0.4" frequencyHz="90.0" durationMs="30"/>
+                        </waveform-envelope-effect>
+                    </repeating>
+                </repeating-effect>
+                </vibration-effect>
+                """;
+
+        assertPublicApisParserSucceeds(xml, effect);
+        assertPublicApisSerializerSucceeds(effect, "0.3", "0.4", "70.0", "80.0", "90.0", "25",
+                "30");
+        assertPublicApisRoundTrip(effect);
+
+        assertHiddenApisParserSucceeds(xml, effect);
+        assertHiddenApisSerializerSucceeds(effect, "0.3", "0.4", "70.0", "80.0", "90.0", "25",
+                "30");
+        assertHiddenApisRoundTrip(effect);
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
+    public void testRepeating_withBasicEnvelopeEffect_allSucceed() throws Exception {
+        VibrationEffect preamble = new VibrationEffect.BasicEnvelopeBuilder()
+                .addControlPoint(0.1f, 0.1f, 10)
+                .addControlPoint(0.2f, 0.2f, 20)
+                .addControlPoint(0.0f, 0.2f, 20)
+                .build();
+        VibrationEffect repeating = new VibrationEffect.BasicEnvelopeBuilder()
+                .setInitialSharpness(0.3f)
+                .addControlPoint(0.3f, 0.4f, 25)
+                .addControlPoint(0.4f, 0.6f, 30)
+                .addControlPoint(0.0f, 0.7f, 35)
+                .build();
+        VibrationEffect effect = VibrationEffect.createRepeatingEffect(preamble, repeating);
+
+        String xml = """
+                <vibration-effect>
+                <repeating-effect>
+                    <preamble>
+                        <basic-envelope-effect>
+                            <control-point intensity="0.1" sharpness="0.1" durationMs="10" />
+                            <control-point intensity="0.2" sharpness="0.2" durationMs="20" />
+                            <control-point intensity="0.0" sharpness="0.2" durationMs="20" />
+                        </basic-envelope-effect>
+                    </preamble>
+                    <repeating>
+                        <basic-envelope-effect initialSharpness="0.3">
+                            <control-point intensity="0.3" sharpness="0.4" durationMs="25" />
+                            <control-point intensity="0.4" sharpness="0.6" durationMs="30" />
+                            <control-point intensity="0.0" sharpness="0.7" durationMs="35" />
+                        </basic-envelope-effect>
+                    </repeating>
+                </repeating-effect>
+                </vibration-effect>
+                """;
+
+        assertPublicApisParserSucceeds(xml, effect);
+        assertPublicApisSerializerSucceeds(effect, "0.0", "0.1", "0.2", "0.3", "0.4", "0.1", "0.2",
+                "0.3", "0.4", "0.6", "0.7", "10", "20", "25", "30", "35");
+        assertPublicApisRoundTrip(effect);
+
+        assertHiddenApisParserSucceeds(xml, effect);
+        assertHiddenApisSerializerSucceeds(effect, "0.0", "0.1", "0.2", "0.3", "0.4", "0.1", "0.2",
+                "0.3", "0.4", "0.6", "0.7", "10", "20", "25", "30", "35");
+        assertHiddenApisRoundTrip(effect);
+
+        effect = VibrationEffect.createRepeatingEffect(repeating);
+
+        xml = """
+                <vibration-effect>
+                <repeating-effect>
+                    <repeating>
+                        <basic-envelope-effect initialSharpness="0.3">
+                            <control-point intensity="0.3" sharpness="0.4" durationMs="25" />
+                            <control-point intensity="0.4" sharpness="0.6" durationMs="30" />
+                            <control-point intensity="0.0" sharpness="0.7" durationMs="35" />
+                        </basic-envelope-effect>
+                    </repeating>
+                </repeating-effect>
+                </vibration-effect>
+                """;
+
+        assertPublicApisParserSucceeds(xml, effect);
+        assertPublicApisSerializerSucceeds(effect, "0.3", "0.4", "0.0", "0.4", "0.6", "0.7", "25",
+                "30", "35");
+        assertPublicApisRoundTrip(effect);
+
+        assertHiddenApisParserSucceeds(xml, effect);
+        assertHiddenApisSerializerSucceeds(effect, "0.3", "0.4", "0.0", "0.4", "0.6", "0.7", "25",
+                "30", "35");
+        assertHiddenApisRoundTrip(effect);
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
+    public void testRepeating_withPredefinedEffects_allSucceed() throws Exception {
+        for (Map.Entry<String, Integer> entry : createPublicPredefinedEffectsMap().entrySet()) {
+            VibrationEffect preamble = VibrationEffect.get(entry.getValue());
+            VibrationEffect repeating = VibrationEffect.get(entry.getValue());
+            VibrationEffect effect = VibrationEffect.createRepeatingEffect(preamble, repeating);
+            String xml = String.format("""
+                    <vibration-effect>
+                        <repeating-effect>
+                            <preamble>
+                                <predefined-effect name="%s"/>
+                            </preamble>
+                            <repeating>
+                                <predefined-effect name="%s"/>
+                            </repeating>
+                        </repeating-effect>
+                    </vibration-effect>
+                    """,
+                    entry.getKey(), entry.getKey());
+
+            assertPublicApisParserSucceeds(xml, effect);
+            assertPublicApisSerializerSucceeds(effect, entry.getKey());
+            assertPublicApisRoundTrip(effect);
+
+            assertHiddenApisParserSucceeds(xml, effect);
+            assertHiddenApisSerializerSucceeds(effect, entry.getKey());
+            assertHiddenApisRoundTrip(effect);
+
+            effect = VibrationEffect.createRepeatingEffect(repeating);
+            xml = String.format("""
+                    <vibration-effect>
+                        <repeating-effect>
+                            <repeating>
+                                <predefined-effect name="%s"/>
+                            </repeating>
+                        </repeating-effect>
+                    </vibration-effect>
+                    """,
+                    entry.getKey());
+
+            assertPublicApisParserSucceeds(xml, effect);
+            assertPublicApisSerializerSucceeds(effect, entry.getKey());
+            assertPublicApisRoundTrip(effect);
+
+            assertHiddenApisParserSucceeds(xml, effect);
+            assertHiddenApisSerializerSucceeds(effect, entry.getKey());
+            assertHiddenApisRoundTrip(effect);
+        }
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
+    public void testRepeating_withWaveformEntry_allSucceed() throws Exception {
+        VibrationEffect preamble = VibrationEffect.createWaveform(new long[]{123, 456, 789, 0},
+                new int[]{254, 1, 255, 0}, /* repeat= */ -1);
+        VibrationEffect repeating = VibrationEffect.createWaveform(new long[]{123, 456, 789, 0},
+                new int[]{254, 1, 255, 0}, /* repeat= */ -1);
+        VibrationEffect effect = VibrationEffect.createRepeatingEffect(preamble, repeating);
+
+        String xml = """
+                <vibration-effect>
+                    <repeating-effect>
+                        <preamble>
+                            <waveform-entry durationMs="123" amplitude="254"/>
+                            <waveform-entry durationMs="456" amplitude="1"/>
+                            <waveform-entry durationMs="789" amplitude="255"/>
+                            <waveform-entry durationMs="0" amplitude="0"/>
+                        </preamble>
+                        <repeating>
+                            <waveform-entry durationMs="123" amplitude="254"/>
+                            <waveform-entry durationMs="456" amplitude="1"/>
+                            <waveform-entry durationMs="789" amplitude="255"/>
+                            <waveform-entry durationMs="0" amplitude="0"/>
+                        </repeating>
+                    </repeating-effect>
+                </vibration-effect>
+                """;
+
+        assertPublicApisParserSucceeds(xml, effect);
+        assertPublicApisSerializerSucceeds(effect, "123", "456", "789", "254", "1", "255", "0");
+        assertPublicApisRoundTrip(effect);
+
+        assertHiddenApisParserSucceeds(xml, effect);
+        assertHiddenApisSerializerSucceeds(effect, "123", "456", "789", "254", "1", "255", "0");
+        assertHiddenApisRoundTrip(effect);
+
+        xml = """
+                <vibration-effect>
+                    <repeating-effect>
+                        <repeating>
+                            <waveform-entry durationMs="123" amplitude="254"/>
+                            <waveform-entry durationMs="456" amplitude="1"/>
+                            <waveform-entry durationMs="789" amplitude="255"/>
+                            <waveform-entry durationMs="0" amplitude="0"/>
+                        </repeating>
+                    </repeating-effect>
+                </vibration-effect>
+                """;
+
+        effect = VibrationEffect.createRepeatingEffect(repeating);
+
+        assertPublicApisParserSucceeds(xml, effect);
+        assertPublicApisSerializerSucceeds(effect, "123", "456", "789", "254", "1", "255", "0");
+        assertPublicApisRoundTrip(effect);
+
+        assertHiddenApisParserSucceeds(xml, effect);
+        assertHiddenApisSerializerSucceeds(effect, "123", "456", "789", "254", "1", "255", "0");
+        assertHiddenApisRoundTrip(effect);
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
+    public void testRepeating_withPrimitives_allSucceed() throws Exception {
+        VibrationEffect preamble = VibrationEffect.startComposition()
+                .addPrimitive(PRIMITIVE_CLICK)
+                .addPrimitive(PRIMITIVE_TICK, 0.2497f)
+                .addPrimitive(PRIMITIVE_LOW_TICK, 1f, 356)
+                .addPrimitive(PRIMITIVE_SPIN, 0.6364f, 7)
+                .compose();
+        VibrationEffect repeating = VibrationEffect.startComposition()
+                .addPrimitive(PRIMITIVE_CLICK)
+                .addPrimitive(PRIMITIVE_TICK, 0.2497f)
+                .addPrimitive(PRIMITIVE_LOW_TICK, 1f, 356)
+                .addPrimitive(PRIMITIVE_SPIN, 0.6364f, 7)
+                .compose();
+        VibrationEffect effect = VibrationEffect.createRepeatingEffect(preamble, repeating);
+
+        String xml = """
+                <vibration-effect>
+                    <repeating-effect>
+                        <preamble>
+                            <primitive-effect name="click" />
+                            <primitive-effect name="tick" scale="0.2497" />
+                            <primitive-effect name="low_tick" delayMs="356" />
+                            <primitive-effect name="spin" scale="0.6364" delayMs="7" />
+                        </preamble>
+                        <repeating>
+                            <primitive-effect name="click" />
+                            <primitive-effect name="tick" scale="0.2497" />
+                            <primitive-effect name="low_tick" delayMs="356" />
+                            <primitive-effect name="spin" scale="0.6364" delayMs="7" />
+                        </repeating>
+                    </repeating-effect>
+                </vibration-effect>
+                """;
+
+        assertPublicApisParserSucceeds(xml, effect);
+        assertPublicApisSerializerSucceeds(effect, "click", "tick", "low_tick", "spin");
+        assertPublicApisRoundTrip(effect);
+
+        assertHiddenApisParserSucceeds(xml, effect);
+        assertHiddenApisSerializerSucceeds(effect, "click", "tick", "low_tick", "spin");
+        assertHiddenApisRoundTrip(effect);
+
+        repeating = VibrationEffect.startComposition()
+                .addPrimitive(PRIMITIVE_CLICK)
+                .addPrimitive(PRIMITIVE_TICK, 0.2497f)
+                .addPrimitive(PRIMITIVE_LOW_TICK, 1f, 356)
+                .addPrimitive(PRIMITIVE_SPIN, 0.6364f, 7)
+                .compose();
+        effect = VibrationEffect.createRepeatingEffect(repeating);
+
+        xml = """
+                <vibration-effect>
+                    <repeating-effect>
+                        <repeating>
+                            <primitive-effect name="click" />
+                            <primitive-effect name="tick" scale="0.2497" />
+                            <primitive-effect name="low_tick" delayMs="356" />
+                            <primitive-effect name="spin" scale="0.6364" delayMs="7" />
+                        </repeating>
+                    </repeating-effect>
+                </vibration-effect>
+                """;
+
+        assertPublicApisParserSucceeds(xml, effect);
+        assertPublicApisSerializerSucceeds(effect, "click", "tick", "low_tick", "spin");
+        assertPublicApisRoundTrip(effect);
+
+        assertHiddenApisParserSucceeds(xml, effect);
+        assertHiddenApisSerializerSucceeds(effect, "click", "tick", "low_tick", "spin");
+        assertHiddenApisRoundTrip(effect);
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
+    public void testRepeating_withMixedVibrations_allSucceed() throws Exception {
+        VibrationEffect preamble = new VibrationEffect.WaveformEnvelopeBuilder()
+                .addControlPoint(0.1f, 50f, 10)
+                .build();
+        VibrationEffect repeating = VibrationEffect.get(VibrationEffect.EFFECT_TICK);
+        VibrationEffect effect = VibrationEffect.createRepeatingEffect(preamble, repeating);
+        String xml = """
+                    <vibration-effect>
+                        <repeating-effect>
+                            <preamble>
+                                <waveform-envelope-effect>
+                                <control-point amplitude="0.1" frequencyHz="50.0" durationMs="10"/>
+                                </waveform-envelope-effect>
+                            </preamble>
+                            <repeating>
+                                <predefined-effect name="tick"/>
+                            </repeating>
+                        </repeating-effect>
+                    </vibration-effect>
+                    """;
+        assertPublicApisParserSucceeds(xml, effect);
+        assertPublicApisSerializerSucceeds(effect, "0.1", "50.0", "10", "tick");
+        assertPublicApisRoundTrip(effect);
+
+        assertHiddenApisParserSucceeds(xml, effect);
+        assertHiddenApisSerializerSucceeds(effect, "0.1", "50.0", "10", "tick");
+        assertHiddenApisRoundTrip(effect);
+
+        preamble = VibrationEffect.createWaveform(new long[]{123, 456},
+                new int[]{254, 1}, /* repeat= */ -1);
+        repeating = new VibrationEffect.BasicEnvelopeBuilder()
+                .addControlPoint(0.3f, 0.4f, 25)
+                .addControlPoint(0.0f, 0.5f, 30)
+                .build();
+        effect = VibrationEffect.createRepeatingEffect(preamble, repeating);
+
+        xml = """
+                <vibration-effect>
+                    <repeating-effect>
+                        <preamble>
+                            <waveform-entry durationMs="123" amplitude="254"/>
+                            <waveform-entry durationMs="456" amplitude="1"/>
+                        </preamble>
+                        <repeating>
+                        <basic-envelope-effect>
+                            <control-point intensity="0.3" sharpness="0.4" durationMs="25" />
+                            <control-point intensity="0.0" sharpness="0.5" durationMs="30" />
+                        </basic-envelope-effect>
+                        </repeating>
+                    </repeating-effect>
+                </vibration-effect>
+                """;
+
+        assertPublicApisParserSucceeds(xml, effect);
+        assertPublicApisSerializerSucceeds(effect, "123", "456", "254", "1", "0.3", "0.0", "0.4",
+                "0.5", "25", "30");
+        assertPublicApisRoundTrip(effect);
+
+        assertHiddenApisParserSucceeds(xml, effect);
+        assertHiddenApisSerializerSucceeds(effect, "123", "456", "254", "1", "0.3", "0.0", "0.4",
+                "0.5", "25", "30");
+        assertHiddenApisRoundTrip(effect);
+
+        preamble = VibrationEffect.startComposition()
+                .addPrimitive(PRIMITIVE_CLICK)
+                .compose();
+        effect = VibrationEffect.createRepeatingEffect(preamble, repeating);
+
+        xml = """
+                <vibration-effect>
+                    <repeating-effect>
+                        <preamble>
+                            <primitive-effect name="click" />
+                        </preamble>
+                        <repeating>
+                            <basic-envelope-effect>
+                                <control-point intensity="0.3" sharpness="0.4" durationMs="25" />
+                                <control-point intensity="0.0" sharpness="0.5" durationMs="30" />
+                            </basic-envelope-effect>
+                        </repeating>
+                    </repeating-effect>
+                </vibration-effect>
+                """;
+
+        assertPublicApisParserSucceeds(xml, effect);
+        assertPublicApisSerializerSucceeds(effect, "click", "0.3", "0.4", "0.0", "0.5", "25", "30");
+        assertPublicApisRoundTrip(effect);
+
+        assertHiddenApisParserSucceeds(xml, effect);
+        assertHiddenApisSerializerSucceeds(effect, "click", "0.3", "0.4", "0.0", "0.5", "25", "30");
+        assertHiddenApisRoundTrip(effect);
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
+    public void testRepeating_badXml_throwsException() throws IOException {
+        // Incomplete XML
+        assertParseElementFails("""
+                <vibration-effect>
+                <repeating-effect>
+                    <preamble>
+                        <primitive-effect name="click" />
+                    </preamble>
+                    <repeating>
+                        <primitive-effect name="click" />
+                """);
+
+        assertParseElementFails("""
+                <vibration-effect>
+                <repeating-effect>
+                        <primitive-effect name="click" />
+                    <repeating>
+                        <primitive-effect name="click" />
+                    </repeating>
+                </repeating-effect>
+                </vibration-effect>
+                """);
+
+        assertParseElementFails("""
+                <vibration-effect>
+                <repeating-effect>
+                    <preamble>
+                        <primitive-effect name="click" />
+                    </preamble>
+                        <primitive-effect name="click" />
+                </repeating-effect>
+                </vibration-effect>
+                """);
+
+        // Bad vibration XML
+        assertParseElementFails("""
+                <vibration-effect>
+                <repeating-effect>
+                    <repeating>
+                        <primitive-effect name="click" />
+                    </repeating>
+                    <preamble>
+                        <primitive-effect name="click" />
+                    </preamble>
+                </repeating-effect>
+                </vibration-effect>
+                """);
+
+        assertParseElementFails("""
+                <vibration-effect>
+                <repeating-effect>
+                    <repeating>
+                    <preamble>
+                        <primitive-effect name="click" />
+                    </preamble>
+                        <primitive-effect name="click" />
+                    </repeating>
+                </repeating-effect>
+                </vibration-effect>
+                """);
+
+        assertParseElementFails("""
+                <vibration-effect>
+                <repeating-effect>
+                    <preamble>
+                        <primitive-effect name="click" />
+                    <repeating>
+                        <primitive-effect name="click" />
+                    </repeating>
+                    </preamble>
+                </repeating-effect>
+                </vibration-effect>
+                """);
+
+        assertParseElementFails("""
+                <vibration-effect>
+                <repeating-effect>
+                    <primitive-effect name="click" />
+                    <primitive-effect name="click" />
+                </repeating-effect>
+                </vibration-effect>
+                """);
+    }
+
+    @Test
+    @DisableFlags(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
+    public void testRepeatingEffect_featureFlagDisabled_allFail() throws Exception {
+        VibrationEffect repeating = VibrationEffect.startComposition()
+                .addPrimitive(PRIMITIVE_CLICK)
+                .addPrimitive(PRIMITIVE_TICK, 0.2497f)
+                .addPrimitive(PRIMITIVE_LOW_TICK, 1f, 356)
+                .addPrimitive(PRIMITIVE_SPIN, 0.6364f, 7)
+                .compose();
+        VibrationEffect effect = VibrationEffect.createRepeatingEffect(repeating);
+
+        String xml = """
+                <vibration-effect>
+                    <repeating-effect>
+                        <repeating>
+                            <primitive-effect name="click" />
+                            <primitive-effect name="tick" scale="0.2497" />
+                            <primitive-effect name="low_tick" delayMs="356" />
+                            <primitive-effect name="spin" scale="0.6364" delayMs="7" />
+                        </repeating>
+                    </repeating-effect>
+                </vibration-effect>
+                """;
+
+        assertPublicApisParserFails(xml);
+        assertPublicApisSerializerFails(effect);
+        assertHiddenApisParserFails(xml);
+        assertHiddenApisSerializerFails(effect);
+    }
+
+    @Test
     @EnableFlags(Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
     public void testVendorEffect_allSucceed() throws Exception {
         PersistableBundle vendorData = new PersistableBundle();
diff --git a/core/xsd/vibrator/vibration/schema/current.txt b/core/xsd/vibrator/vibration/schema/current.txt
index 29f8d19..89ca044 100644
--- a/core/xsd/vibrator/vibration/schema/current.txt
+++ b/core/xsd/vibrator/vibration/schema/current.txt
@@ -62,17 +62,41 @@
     enum_constant public static final com.android.internal.vibrator.persistence.PrimitiveEffectName tick;
   }
 
+  public class RepeatingEffect {
+    ctor public RepeatingEffect();
+    method public com.android.internal.vibrator.persistence.RepeatingEffectEntry getPreamble();
+    method public com.android.internal.vibrator.persistence.RepeatingEffectEntry getRepeating();
+    method public void setPreamble(com.android.internal.vibrator.persistence.RepeatingEffectEntry);
+    method public void setRepeating(com.android.internal.vibrator.persistence.RepeatingEffectEntry);
+  }
+
+  public class RepeatingEffectEntry {
+    ctor public RepeatingEffectEntry();
+    method public com.android.internal.vibrator.persistence.BasicEnvelopeEffect getBasicEnvelopeEffect_optional();
+    method public com.android.internal.vibrator.persistence.PredefinedEffect getPredefinedEffect_optional();
+    method public com.android.internal.vibrator.persistence.PrimitiveEffect getPrimitiveEffect_optional();
+    method public com.android.internal.vibrator.persistence.WaveformEntry getWaveformEntry_optional();
+    method public com.android.internal.vibrator.persistence.WaveformEnvelopeEffect getWaveformEnvelopeEffect_optional();
+    method public void setBasicEnvelopeEffect_optional(com.android.internal.vibrator.persistence.BasicEnvelopeEffect);
+    method public void setPredefinedEffect_optional(com.android.internal.vibrator.persistence.PredefinedEffect);
+    method public void setPrimitiveEffect_optional(com.android.internal.vibrator.persistence.PrimitiveEffect);
+    method public void setWaveformEntry_optional(com.android.internal.vibrator.persistence.WaveformEntry);
+    method public void setWaveformEnvelopeEffect_optional(com.android.internal.vibrator.persistence.WaveformEnvelopeEffect);
+  }
+
   public class VibrationEffect {
     ctor public VibrationEffect();
     method public com.android.internal.vibrator.persistence.BasicEnvelopeEffect getBasicEnvelopeEffect_optional();
     method public com.android.internal.vibrator.persistence.PredefinedEffect getPredefinedEffect_optional();
     method public com.android.internal.vibrator.persistence.PrimitiveEffect getPrimitiveEffect_optional();
+    method public com.android.internal.vibrator.persistence.RepeatingEffect getRepeatingEffect_optional();
     method public byte[] getVendorEffect_optional();
     method public com.android.internal.vibrator.persistence.WaveformEffect getWaveformEffect_optional();
     method public com.android.internal.vibrator.persistence.WaveformEnvelopeEffect getWaveformEnvelopeEffect_optional();
     method public void setBasicEnvelopeEffect_optional(com.android.internal.vibrator.persistence.BasicEnvelopeEffect);
     method public void setPredefinedEffect_optional(com.android.internal.vibrator.persistence.PredefinedEffect);
     method public void setPrimitiveEffect_optional(com.android.internal.vibrator.persistence.PrimitiveEffect);
+    method public void setRepeatingEffect_optional(com.android.internal.vibrator.persistence.RepeatingEffect);
     method public void setVendorEffect_optional(byte[]);
     method public void setWaveformEffect_optional(com.android.internal.vibrator.persistence.WaveformEffect);
     method public void setWaveformEnvelopeEffect_optional(com.android.internal.vibrator.persistence.WaveformEnvelopeEffect);
diff --git a/core/xsd/vibrator/vibration/vibration-plus-hidden-apis.xsd b/core/xsd/vibrator/vibration/vibration-plus-hidden-apis.xsd
index b4df2d1..57bcde7 100644
--- a/core/xsd/vibrator/vibration/vibration-plus-hidden-apis.xsd
+++ b/core/xsd/vibrator/vibration/vibration-plus-hidden-apis.xsd
@@ -60,6 +60,30 @@
             <!-- Basic envelope effect -->
             <xs:element name="basic-envelope-effect" type="BasicEnvelopeEffect"/>
 
+            <!-- Repeating vibration effect -->
+            <xs:element name="repeating-effect" type="RepeatingEffect"/>
+
+        </xs:choice>
+    </xs:complexType>
+
+    <xs:complexType name="RepeatingEffect">
+        <xs:sequence>
+            <xs:element name="preamble" maxOccurs="1" minOccurs="0" type="RepeatingEffectEntry" />
+            <xs:element name="repeating" maxOccurs="1" minOccurs="1" type="RepeatingEffectEntry" />
+        </xs:sequence>
+    </xs:complexType>
+
+    <xs:complexType name="RepeatingEffectEntry">
+        <xs:choice>
+            <xs:element name="predefined-effect" type="PredefinedEffect" />
+            <xs:element name="waveform-envelope-effect" type="WaveformEnvelopeEffect" />
+            <xs:element name="basic-envelope-effect" type="BasicEnvelopeEffect" />
+            <xs:sequence>
+                <xs:element name="waveform-entry" type="WaveformEntry" />
+            </xs:sequence>
+            <xs:sequence>
+                <xs:element name="primitive-effect" type="PrimitiveEffect" />
+            </xs:sequence>
         </xs:choice>
     </xs:complexType>
 
diff --git a/core/xsd/vibrator/vibration/vibration.xsd b/core/xsd/vibrator/vibration/vibration.xsd
index fba966f..c11fb66 100644
--- a/core/xsd/vibrator/vibration/vibration.xsd
+++ b/core/xsd/vibrator/vibration/vibration.xsd
@@ -58,9 +58,34 @@
             <!-- Basic envelope effect -->
             <xs:element name="basic-envelope-effect" type="BasicEnvelopeEffect"/>
 
+            <!-- Repeating vibration effect -->
+            <xs:element name="repeating-effect" type="RepeatingEffect"/>
+
         </xs:choice>
     </xs:complexType>
 
+    <xs:complexType name="RepeatingEffect">
+        <xs:sequence>
+            <xs:element name="preamble" maxOccurs="1" minOccurs="0" type="RepeatingEffectEntry" />
+            <xs:element name="repeating" maxOccurs="1" minOccurs="1" type="RepeatingEffectEntry" />
+        </xs:sequence>
+    </xs:complexType>
+
+    <xs:complexType name="RepeatingEffectEntry">
+        <xs:choice>
+            <xs:element name="predefined-effect" type="PredefinedEffect" />
+            <xs:element name="waveform-envelope-effect" type="WaveformEnvelopeEffect" />
+            <xs:element name="basic-envelope-effect" type="BasicEnvelopeEffect" />
+            <xs:sequence>
+                <xs:element name="waveform-entry" type="WaveformEntry" />
+            </xs:sequence>
+            <xs:sequence>
+                <xs:element name="primitive-effect" type="PrimitiveEffect" />
+            </xs:sequence>
+        </xs:choice>
+    </xs:complexType>
+
+
     <xs:complexType name="WaveformEffect">
         <xs:sequence>
 
diff --git a/data/etc/com.android.systemui.xml b/data/etc/com.android.systemui.xml
index 38ea4ac..45952ea 100644
--- a/data/etc/com.android.systemui.xml
+++ b/data/etc/com.android.systemui.xml
@@ -94,5 +94,6 @@
         <permission name="android.permission.CONTROL_UI_TRACING" />
         <permission name="android.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND" />
         <permission name="android.permission.OVERRIDE_SYSTEM_KEY_BEHAVIOR_IN_FOCUSED_WINDOW"/>
+        <permission name="android.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE" />
     </privapp-permissions>
 </permissions>
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index a26f5e3..2398e71 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -419,6 +419,7 @@
         <permission name="android.permission.REQUEST_COMPANION_PROFILE_APP_STREAMING" />
         <permission name="android.permission.REQUEST_COMPANION_PROFILE_WATCH" />
         <permission name="android.permission.REQUEST_COMPANION_PROFILE_NEARBY_DEVICE_STREAMING" />
+        <permission name="android.permission.REQUEST_COMPANION_PROFILE_SENSOR_DEVICE_STREAMING" />
         <permission name="android.permission.REQUEST_COMPANION_PROFILE_COMPUTER" />
         <permission name="android.permission.REQUEST_COMPANION_SELF_MANAGED" />
         <permission name="android.permission.REQUEST_OBSERVE_DEVICE_UUID_PRESENCE" />
@@ -606,6 +607,8 @@
         <!-- Permission required for CTS test - IntrusionDetectionManagerTest -->
         <permission name="android.permission.READ_INTRUSION_DETECTION_STATE" />
         <permission name="android.permission.MANAGE_INTRUSION_DETECTION_STATE" />
+        <!-- Permission required for CTS test - KeyguardLockedStateApiTest -->
+        <permission name="android.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE" />
     </privapp-permissions>
 
     <privapp-permissions package="com.android.statementservice">
@@ -676,4 +679,8 @@
         <permission name="android.permission.BATTERY_STATS"/>
         <permission name="android.permission.ENTER_TRADE_IN_MODE"/>
     </privapp-permissions>
+
+    <privapp-permissions package="com.android.wm.shell">
+        <permission name="android.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE" />
+    </privapp-permissions>
 </permissions>
diff --git a/framework-jarjar-rules.txt b/framework-jarjar-rules.txt
index 087378b..3da69e8 100644
--- a/framework-jarjar-rules.txt
+++ b/framework-jarjar-rules.txt
@@ -10,3 +10,6 @@
 
 # For Perfetto proto dependencies
 rule perfetto.protos.** android.internal.perfetto.protos.@1
+
+# For aconfig storage classes
+rule android.aconfig.storage.** android.internal.aconfig.storage.@1
diff --git a/graphics/java/android/graphics/BLASTBufferQueue.java b/graphics/java/android/graphics/BLASTBufferQueue.java
index 90723b2..906c71d 100644
--- a/graphics/java/android/graphics/BLASTBufferQueue.java
+++ b/graphics/java/android/graphics/BLASTBufferQueue.java
@@ -49,11 +49,22 @@
     private static native void nativeSetTransactionHangCallback(long ptr,
             TransactionHangCallback callback);
     private static native void nativeSetApplyToken(long ptr, IBinder applyToken);
+    private static native void nativeSetWaitForBufferReleaseCallback(long ptr,
+            WaitForBufferReleaseCallback callback);
 
     public interface TransactionHangCallback {
         void onTransactionHang(String reason);
     }
 
+
+    public interface WaitForBufferReleaseCallback {
+        /**
+         * Indicates that the client is waiting on buffer release
+         * due to no free buffers being available to render into.
+         */
+        void onWaitForBufferRelease();
+    }
+
     /** Create a new connection with the surface flinger. */
     public BLASTBufferQueue(String name, SurfaceControl sc, int width, int height,
             @PixelFormat.Format int format) {
@@ -210,4 +221,11 @@
     public void setApplyToken(IBinder applyToken) {
         nativeSetApplyToken(mNativeObject, applyToken);
     }
+
+    /**
+     * Propagate callback about being blocked on buffer release.
+     */
+    public void setWaitForBufferReleaseCallback(WaitForBufferReleaseCallback waitCallback) {
+        nativeSetWaitForBufferReleaseCallback(mNativeObject, waitCallback);
+    }
 }
diff --git a/libs/WindowManager/Shell/aconfig/multitasking.aconfig b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
index 714d5e0..5f1fb4b 100644
--- a/libs/WindowManager/Shell/aconfig/multitasking.aconfig
+++ b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
@@ -132,13 +132,6 @@
 }
 
 flag {
-    name: "enable_bubble_bar_in_persistent_task_bar"
-    namespace: "multitasking"
-    description: "Enable bubble bar to be shown in the persistent task bar"
-    bug: "346391377"
-}
-
-flag {
     name: "bubble_view_info_executors"
     namespace: "multitasking"
     description: "Use executors to inflate bubble views"
diff --git a/libs/WindowManager/Shell/res/layout/bubble_bar_menu_view.xml b/libs/WindowManager/Shell/res/layout/bubble_bar_menu_view.xml
index 27e3b00..cef4f5f 100644
--- a/libs/WindowManager/Shell/res/layout/bubble_bar_menu_view.xml
+++ b/libs/WindowManager/Shell/res/layout/bubble_bar_menu_view.xml
@@ -51,7 +51,8 @@
             android:layout_marginStart="8dp"
             android:layout_weight="1"
             android:textColor="@androidprv:color/materialColorOnSurface"
-            android:textAppearance="@*android:style/TextAppearance.DeviceDefault" />
+            android:textAppearance="@*android:style/TextAppearance.DeviceDefault"
+            android:textDirection="locale" />
 
         <ImageView
             android:id="@+id/bubble_bar_manage_menu_dismiss_icon"
diff --git a/libs/WindowManager/Shell/res/layout/desktop_header_maximize_menu_button_progress_indicator_layout.xml b/libs/WindowManager/Shell/res/layout/desktop_header_maximize_menu_button_progress_indicator_layout.xml
new file mode 100644
index 0000000..5a39c83
--- /dev/null
+++ b/libs/WindowManager/Shell/res/layout/desktop_header_maximize_menu_button_progress_indicator_layout.xml
@@ -0,0 +1,34 @@
+<?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"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="44dp"
+    android:layout_height="40dp"
+    android:importantForAccessibility="noHideDescendants">
+    <ProgressBar
+        android:id="@+id/progress_bar"
+        style="?android:attr/progressBarStyleHorizontal"
+        android:progressDrawable="@drawable/circular_progress"
+        android:layout_width="32dp"
+        android:layout_height="32dp"
+        android:indeterminate="false"
+        android:layout_marginHorizontal="6dp"
+        android:layout_marginVertical="4dp"
+        android:visibility="invisible"/>
+</FrameLayout>
diff --git a/libs/WindowManager/Shell/res/layout/maximize_menu_button.xml b/libs/WindowManager/Shell/res/layout/maximize_menu_button.xml
index b734d2d..059e9e1 100644
--- a/libs/WindowManager/Shell/res/layout/maximize_menu_button.xml
+++ b/libs/WindowManager/Shell/res/layout/maximize_menu_button.xml
@@ -17,21 +17,14 @@
 <merge xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
 
-    <FrameLayout
+    <ViewStub
+        android:id="@+id/stub_progress_bar_container"
+        android:inflatedId="@+id/inflatedProgressBarContainer"
+        android:layout="@layout/desktop_header_maximize_menu_button_progress_indicator_layout"
         android:layout_width="44dp"
         android:layout_height="40dp"
-        android:importantForAccessibility="noHideDescendants">
-        <ProgressBar
-            android:id="@+id/progress_bar"
-            style="?android:attr/progressBarStyleHorizontal"
-            android:progressDrawable="@drawable/circular_progress"
-            android:layout_width="32dp"
-            android:layout_height="32dp"
-            android:indeterminate="false"
-            android:layout_marginHorizontal="6dp"
-            android:layout_marginVertical="4dp"
-            android:visibility="invisible"/>
-    </FrameLayout>
+        android:importantForAccessibility="noHideDescendants"
+        />
 
     <ImageButton
         android:id="@+id/maximize_window"
diff --git a/libs/WindowManager/Shell/shared/Android.bp b/libs/WindowManager/Shell/shared/Android.bp
index 5113d98..c3ee0f7 100644
--- a/libs/WindowManager/Shell/shared/Android.bp
+++ b/libs/WindowManager/Shell/shared/Android.bp
@@ -71,6 +71,7 @@
     srcs: [
         "**/desktopmode/*.java",
         "**/desktopmode/*.kt",
+        ":wm_shell-shared-utils",
     ],
     static_libs: [
         "com.android.window.flags.window-aconfig-java",
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 4c77eaf..755f472 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
@@ -16,9 +16,15 @@
 
 package com.android.wm.shell.shared.desktopmode;
 
+import static android.hardware.display.DisplayManager.DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED;
+
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.Context;
+import android.hardware.display.DisplayManager;
 import android.os.SystemProperties;
+import android.view.Display;
+import android.view.WindowManager;
 import android.window.DesktopModeFlags;
 
 import com.android.internal.R;
@@ -26,6 +32,7 @@
 import com.android.window.flags.Flags;
 
 import java.io.PrintWriter;
+import java.util.Arrays;
 
 /**
  * Constants for desktop mode feature
@@ -35,6 +42,9 @@
 
     private static final String TAG = "DesktopModeStatus";
 
+    @Nullable
+    private static Boolean sIsLargeScreenDevice = null;
+
     /**
      * Flag to indicate whether task resizing is veiled.
      */
@@ -227,13 +237,12 @@
      * necessarily enabling desktop mode
      */
     public static boolean overridesShowAppHandle(@NonNull Context context) {
-        return Flags.showAppHandleLargeScreens()
-                && context.getResources().getBoolean(R.bool.config_enableAppHandle);
+        return Flags.showAppHandleLargeScreens() && deviceHasLargeScreen(context);
     }
 
     /**
      * @return {@code true} if the app handle should be shown because desktop mode is enabled or
-     * the device is overriding {@code R.bool.config_enableAppHandle}
+     * the device has a large screen
      */
     public static boolean canEnterDesktopModeOrShowAppHandle(@NonNull Context context) {
         return canEnterDesktopMode(context) || overridesShowAppHandle(context);
@@ -276,6 +285,21 @@
     }
 
     /**
+     * @return {@code true} if this device has an internal large screen
+     */
+    private static boolean deviceHasLargeScreen(@NonNull Context context) {
+        if (sIsLargeScreenDevice == null) {
+            sIsLargeScreenDevice = Arrays.stream(
+                context.getSystemService(DisplayManager.class)
+                        .getDisplays(DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED))
+                .filter(display -> display.getType() == Display.TYPE_INTERNAL)
+                .anyMatch(display -> display.getMinSizeDimensionDp()
+                        >= WindowManager.LARGE_SCREEN_SMALLEST_SCREEN_WIDTH_DP);
+        }
+        return sIsLargeScreenDevice;
+    }
+
+    /**
      * Return {@code true} if a display should enter desktop mode by default when the windowing mode
      * of the display's root [TaskDisplayArea] is set to WINDOWING_MODE_FREEFORM.
      */
@@ -315,6 +339,6 @@
         pw.println(maxTaskLimitHandle == null ? "null" : maxTaskLimitHandle.getInt(/* def= */ -1));
 
         pw.print(innerPrefix); pw.print("showAppHandle config override=");
-        pw.print(context.getResources().getBoolean(R.bool.config_enableAppHandle));
+        pw.print(overridesShowAppHandle(context));
     }
 }
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/split/SplitScreenConstants.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/split/SplitScreenConstants.java
index 86be0d4..b48296f5 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/split/SplitScreenConstants.java
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/split/SplitScreenConstants.java
@@ -58,6 +58,11 @@
      */
     public static final int SPLIT_POSITION_BOTTOM_OR_RIGHT = 1;
 
+    /**
+     * Deprecated and will be replaced fully by @SplitIndex. With support for 3+ apps in split,
+     * existing references to top/left and bottom/right will be replaced by INDEX_0 and INDEX_1
+     * respectively. For now they can be used interchangeably, the underlying ints are the same.
+     */
     @IntDef(prefix = {"SPLIT_POSITION_"}, value = {
             SPLIT_POSITION_UNDEFINED,
             SPLIT_POSITION_TOP_OR_LEFT,
@@ -85,6 +90,21 @@
     public @interface SplitIndex {
     }
 
+    /**
+     * Return the @SplitIndex constant for a given integer index. @SplitIndex is the replacement
+     * for @SplitPosition, and will be used interchangeably with @SplitPosition to support 3+ apps
+     * in split.
+     */
+    public static int getIndex(int i) {
+        return switch (i) {
+            case 0 -> SPLIT_INDEX_0;
+            case 1 -> SPLIT_INDEX_1;
+            case 2 -> SPLIT_INDEX_2;
+            case 3 -> SPLIT_INDEX_3;
+            default -> SPLIT_INDEX_UNDEFINED;
+        };
+    }
+
     /** Signifies that user is currently not in split screen. */
     public static final int NOT_IN_SPLIT = -1;
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/AppToWebUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/AppToWebUtils.kt
index 68c42d6..06a55d3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/AppToWebUtils.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/AppToWebUtils.kt
@@ -24,7 +24,6 @@
 import android.content.Intent
 import android.content.Intent.ACTION_VIEW
 import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
-import android.content.Intent.FLAG_ACTIVITY_REQUIRE_NON_BROWSER
 import android.content.pm.PackageManager
 import android.content.pm.verify.domain.DomainVerificationManager
 import android.content.pm.verify.domain.DomainVerificationUserState
@@ -60,13 +59,15 @@
  * Returns intent if there is a browser application available to handle the uri. Otherwise, returns
  * null.
  */
-fun getBrowserIntent(uri: Uri, packageManager: PackageManager): Intent? {
+fun getBrowserIntent(uri: Uri, packageManager: PackageManager, userId: Int): Intent? {
     val intent = Intent.makeMainSelectorActivity(Intent.ACTION_MAIN, Intent.CATEGORY_APP_BROWSER)
         .setData(uri)
         .addFlags(FLAG_ACTIVITY_NEW_TASK)
-    // If there is no browser application available to handle intent, return null
-    val component = intent.resolveActivity(packageManager) ?: return null
-    intent.setComponent(component)
+    // If there is a browser application available to handle the intent, return the intent.
+    // Otherwise, return null.
+    val resolveInfo = packageManager.resolveActivityAsUser(intent, /* flags= */ 0, userId)
+        ?: return null
+    intent.setComponent(resolveInfo.componentInfo.componentName)
     return intent
 }
 
@@ -74,14 +75,17 @@
  * Returns intent if there is a non-browser application available to handle the uri. Otherwise,
  * returns null.
  */
-fun getAppIntent(uri: Uri, packageManager: PackageManager): Intent? {
-    val intent = Intent(ACTION_VIEW, uri).apply {
-        flags = FLAG_ACTIVITY_NEW_TASK or FLAG_ACTIVITY_REQUIRE_NON_BROWSER
+fun getAppIntent(uri: Uri, packageManager: PackageManager, userId: Int): Intent? {
+    val intent = Intent(ACTION_VIEW, uri).addFlags(FLAG_ACTIVITY_NEW_TASK)
+    val resolveInfo = packageManager.resolveActivityAsUser(intent, /* flags= */ 0, userId)
+        ?: return null
+    // If there is a non-browser application available to handle the intent, return the intent.
+    // Otherwise, return null.
+     if (resolveInfo.activityInfo != null && !resolveInfo.handleAllWebDataURI) {
+        intent.setComponent(resolveInfo.componentInfo.componentName)
+        return intent
     }
-    // If there is no application available to handle intent, return null
-    val component = intent.resolveActivity(packageManager) ?: return null
-    intent.setComponent(component)
-    return intent
+    return null
 }
 
 /**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/OpenByDefaultDialog.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/OpenByDefaultDialog.kt
index a727b54..4cc81a9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/OpenByDefaultDialog.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/OpenByDefaultDialog.kt
@@ -17,7 +17,6 @@
 package com.android.wm.shell.apptoweb
 
 import android.app.ActivityManager.RunningTaskInfo
-import android.app.TaskInfo
 import android.content.Context
 import android.content.pm.verify.domain.DomainVerificationManager
 import android.graphics.Bitmap
@@ -36,8 +35,17 @@
 import android.window.TaskConstants
 import com.android.wm.shell.R
 import com.android.wm.shell.common.DisplayController
+import com.android.wm.shell.shared.annotations.ShellBackgroundThread
+import com.android.wm.shell.shared.annotations.ShellMainThread
 import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalViewHostViewContainer
+import com.android.wm.shell.windowdecor.common.WindowDecorTaskResourceLoader
 import java.util.function.Supplier
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.MainCoroutineDispatcher
+import kotlinx.coroutines.isActive
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
 
 
 /**
@@ -45,13 +53,14 @@
  */
 internal class OpenByDefaultDialog(
     private val context: Context,
-    private val taskInfo: TaskInfo,
+    private val taskInfo: RunningTaskInfo,
     private val taskSurface: SurfaceControl,
     private val displayController: DisplayController,
+    private val taskResourceLoader: WindowDecorTaskResourceLoader,
     private val surfaceControlTransactionSupplier: Supplier<SurfaceControl.Transaction>,
+    @ShellMainThread private val mainDispatcher: MainCoroutineDispatcher,
+    @ShellBackgroundThread private val bgScope: CoroutineScope,
     private val listener: DialogLifecycleListener,
-    appIconBitmap: Bitmap?,
-    appName: CharSequence?
 ) {
     private lateinit var dialog: OpenByDefaultDialogView
     private lateinit var viewHost: SurfaceControlViewHost
@@ -67,11 +76,20 @@
         context.getSystemService(DomainVerificationManager::class.java)!!
     private val packageName = taskInfo.baseActivity?.packageName!!
 
+    private var loadAppInfoJob: Job? = null
 
     init {
         createDialog()
         initializeRadioButtons()
-        bindAppInfo(appIconBitmap, appName)
+        loadAppInfoJob = bgScope.launch {
+            if (!isActive) return@launch
+            val name = taskResourceLoader.getName(taskInfo)
+            val icon = taskResourceLoader.getHeaderIcon(taskInfo)
+            withContext(mainDispatcher.immediate) {
+                if (!isActive) return@withContext
+                bindAppInfo(icon, name)
+            }
+        }
     }
 
     /** Creates an open by default settings dialog. */
@@ -147,14 +165,15 @@
     }
 
     private fun closeMenu() {
+        loadAppInfoJob?.cancel()
         dialogContainer?.releaseView()
         dialogContainer = null
         listener.onDialogDismissed()
     }
 
      private fun bindAppInfo(
-        appIconBitmap: Bitmap?,
-        appName: CharSequence?
+        appIconBitmap: Bitmap,
+        appName: CharSequence
     ) {
         appIconView.setImageBitmap(appIconBitmap)
         appNameView.text = appName
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 56efdb8..ddc107e 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
@@ -1106,11 +1106,13 @@
             mCurrentTracker.updateStartLocation();
             BackMotionEvent startEvent = mCurrentTracker.createStartEvent(mApps[0]);
             dispatchOnBackStarted(mActiveCallback, startEvent);
-            // TODO(b/373544911): onBackStarted is dispatched here so that
-            //  WindowOnBackInvokedDispatcher knows about the back navigation and intercepts touch
-            //  events while it's active. It would be cleaner and safer to disable multitouch
-            //  altogether (same as in gesture-nav).
-            dispatchOnBackStarted(mBackNavigationInfo.getOnBackInvokedCallback(), startEvent);
+            if (startEvent.getSwipeEdge() == EDGE_NONE) {
+                // TODO(b/373544911): onBackStarted is dispatched here so that
+                //  WindowOnBackInvokedDispatcher knows about the back navigation and intercepts
+                //  touch events while it's active. It would be cleaner and safer to disable
+                //  multitouch altogether (same as in gesture-nav).
+                dispatchOnBackStarted(mBackNavigationInfo.getOnBackInvokedCallback(), startEvent);
+            }
         }
     }
 
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 bec73a1..9aba3aa 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
@@ -2072,10 +2072,7 @@
 
         @Override
         public void suppressionChanged(Bubble bubble, boolean isSuppressed) {
-            if (mLayerView != null) {
-                // TODO (b/273316505) handle suppression changes, although might not need to
-                //  to do anything on the layerview side for this...
-            }
+            // Nothing to do for our views, handled by launcher / in the bubble bar.
         }
 
         @Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipAppOpsListener.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipAppOpsListener.kt
index 193c593..2c4df0c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipAppOpsListener.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipAppOpsListener.kt
@@ -25,7 +25,6 @@
 
 class PipAppOpsListener(
     private val mContext: Context,
-    private val mCallback: Callback,
     private val mMainExecutor: ShellExecutor
 ) {
     private val mAppOpsManager: AppOpsManager = checkNotNull(
@@ -46,7 +45,9 @@
                     packageName
                 ) != AppOpsManager.MODE_ALLOWED
             ) {
-                mMainExecutor.execute { mCallback.dismissPip() }
+                mCallback?.let {
+                    mMainExecutor.execute { it.dismissPip() }
+                }
             }
         } catch (e: PackageManager.NameNotFoundException) {
             // Unregister the listener if the package can't be found
@@ -54,6 +55,12 @@
         }
     }
 
+    private var mCallback: Callback? = null
+
+    fun setCallback(callback: Callback) {
+        mCallback = callback
+    }
+
     fun onActivityPinned(packageName: String) {
         // Register for changes to the app ops setting for this package while it is in PiP
         registerAppOpsListener(packageName)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java
index d20ad5d..9fb36b3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java
@@ -33,12 +33,14 @@
 import android.animation.ValueAnimator;
 import android.app.ActivityManager;
 import android.content.Context;
+import android.content.pm.ActivityInfo;
 import android.content.res.Configuration;
 import android.graphics.Color;
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.os.Binder;
+import android.os.Trace;
 import android.view.IWindow;
 import android.view.LayoutInflater;
 import android.view.SurfaceControl;
@@ -50,10 +52,12 @@
 import android.widget.ImageView;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 
 import com.android.launcher3.icons.IconProvider;
 import com.android.wm.shell.R;
 import com.android.wm.shell.common.ScreenshotUtils;
+import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.SurfaceUtils;
 
 import java.util.function.Consumer;
@@ -79,9 +83,19 @@
     private static final String RESIZING_BACKGROUND_SURFACE_NAME = "ResizingBackground";
     private static final String GAP_BACKGROUND_SURFACE_NAME = "GapBackground";
 
+    // Indicates the loading state of mIcon
+    enum IconLoadState {
+        NOT_LOADED,
+        LOADING,
+        LOADED
+    }
+
     private final IconProvider mIconProvider;
+    private final ShellExecutor mMainExecutor;
+    private final ShellExecutor mBgExecutor;
 
     private Drawable mIcon;
+    private IconLoadState mIconLoadState = IconLoadState.NOT_LOADED;
     private ImageView mVeilIconView;
     private SurfaceControlViewHost mViewHost;
     /** The parent surface that this is attached to. Should be the stage root. */
@@ -109,9 +123,14 @@
     private int mOffsetY;
     private int mRunningAnimationCount = 0;
 
-    public SplitDecorManager(Configuration configuration, IconProvider iconProvider) {
+    public SplitDecorManager(Configuration configuration,
+            IconProvider iconProvider,
+            ShellExecutor mainExecutor,
+            ShellExecutor bgExecutor) {
         super(configuration, null /* rootSurface */, null /* hostInputToken */);
         mIconProvider = iconProvider;
+        mMainExecutor = mainExecutor;
+        mBgExecutor = bgExecutor;
     }
 
     @Override
@@ -199,6 +218,7 @@
         }
         mHostLeash = null;
         mIcon = null;
+        mIconLoadState = IconLoadState.NOT_LOADED;
         mVeilIconView = null;
         mIsCurrentlyChanging = false;
         mShown = false;
@@ -260,10 +280,11 @@
                     .setWindowCrop(mGapBackgroundLeash, sideBounds.width(), sideBounds.height());
         }
 
-        if (mIcon == null && resizingTask.topActivityInfo != null) {
-            mIcon = mIconProvider.getIcon(resizingTask.topActivityInfo);
-            mVeilIconView.setImageDrawable(mIcon);
-            mVeilIconView.setVisibility(View.VISIBLE);
+        if (mIconLoadState == IconLoadState.NOT_LOADED && resizingTask.topActivityInfo != null) {
+            loadIconInBackground(resizingTask.topActivityInfo, () -> {
+                mVeilIconView.setImageDrawable(mIcon);
+                mVeilIconView.setVisibility(View.VISIBLE);
+            });
 
             WindowManager.LayoutParams lp =
                     (WindowManager.LayoutParams) mViewHost.getView().getLayoutParams();
@@ -417,10 +438,10 @@
         }
 
         if (mIcon == null && resizingTask.topActivityInfo != null) {
-            // Initialize icon
-            mIcon = mIconProvider.getIcon(resizingTask.topActivityInfo);
-            mVeilIconView.setImageDrawable(mIcon);
-            mVeilIconView.setVisibility(View.VISIBLE);
+            loadIconInBackground(resizingTask.topActivityInfo, () -> {
+                mVeilIconView.setImageDrawable(mIcon);
+                mVeilIconView.setVisibility(View.VISIBLE);
+            });
 
             WindowManager.LayoutParams lp =
                     (WindowManager.LayoutParams) mViewHost.getView().getLayoutParams();
@@ -453,7 +474,7 @@
             return;
         }
 
-        // Recenter icon
+        // Re-center icon
         t.setPosition(mIconLeash,
                 mInstantaneousBounds.width() / 2f - mIconSize / 2f,
                 mInstantaneousBounds.height() / 2f - mIconSize / 2f);
@@ -596,9 +617,38 @@
             mVeilIconView.setImageDrawable(null);
             t.hide(mIconLeash);
             mIcon = null;
+            mIconLoadState = IconLoadState.NOT_LOADED;
         }
     }
 
+    /**
+     * Loads the icon for the given {@param info}, calling {@param postLoadCb} on the main thread
+     * if provided.
+     */
+    private void loadIconInBackground(@NonNull ActivityInfo info, @Nullable Runnable postLoadCb) {
+        mIconLoadState = IconLoadState.LOADING;
+        mBgExecutor.setBoost();
+        mBgExecutor.execute(() -> {
+            Trace.beginSection("SplitDecorManager.loadIconInBackground("
+                    + info.applicationInfo.packageName + ")");
+            final Drawable icon = mIconProvider.getIcon(info);
+            Trace.endSection();
+            mMainExecutor.execute(() -> {
+                if (mIconLoadState != IconLoadState.LOADING) {
+                    // The request was canceled while loading in the background, just drop the
+                    // result
+                    return;
+                }
+                mIcon = icon;
+                mIconLoadState = IconLoadState.LOADED;
+                if (postLoadCb != null) {
+                    postLoadCb.run();
+                }
+            });
+            mBgExecutor.resetBoost();
+        });
+    }
+
     private static float[] getResizingBackgroundColor(ActivityManager.RunningTaskInfo taskInfo) {
         final int taskBgColor = taskInfo.taskDescription.getBackgroundColor();
         return Color.valueOf(taskBgColor == -1 ? Color.WHITE : taskBgColor).getComponents();
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 88c91db..21c44c9 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
@@ -794,6 +794,10 @@
     }
 
     void onStartDragging() {
+        // This triggers initialization of things like the resize veil in preparation for
+        // showing it when the user moves the divider past the slop
+        updateDividerBounds(getDividerPosition(), false /* shouldUseParallaxEffect */);
+
         mInteractionJankMonitor.begin(getDividerLeash(), mContext, mHandler,
                 CUJ_SPLIT_SCREEN_RESIZE);
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/AppCompatUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/AppCompatUtils.kt
index bc56637..d1dcc9b1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/AppCompatUtils.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/AppCompatUtils.kt
@@ -18,22 +18,29 @@
 
 package com.android.wm.shell.compatui
 
-import android.app.TaskInfo
+import android.app.ActivityManager.RunningTaskInfo
 import android.content.Context
 import com.android.internal.R
 
 // TODO(b/347289970): Consider replacing with API
 /**
  * If the top activity should be exempt from desktop windowing and forced back to fullscreen.
- * Currently includes all system ui activities and modal dialogs. However is the top activity is not
+ * Currently includes all system ui activities and modal dialogs. However if the top activity is not
  * being displayed, regardless of its configuration, we will not exempt it as to remain in the
  * desktop windowing environment.
  */
-fun isTopActivityExemptFromDesktopWindowing(context: Context, task: TaskInfo) =
-    (isSystemUiTask(context, task) || (task.numActivities > 0 && task.isActivityStackTransparent))
+fun isTopActivityExemptFromDesktopWindowing(context: Context, task: RunningTaskInfo) =
+    (isSystemUiTask(context, task) || isTransparentTask(task))
             && !task.isTopActivityNoDisplay
 
-private fun isSystemUiTask(context: Context, task: TaskInfo): Boolean {
+/**
+ * Returns true if all activities in a tasks stack are transparent. If there are no activities will
+ * return false.
+ */
+fun isTransparentTask(task: RunningTaskInfo): Boolean = task.isActivityStackTransparent
+        && task.numActivities > 0
+
+private fun isSystemUiTask(context: Context, task: RunningTaskInfo): Boolean {
     val sysUiPackageName: String =
         context.resources.getString(R.string.config_systemUi)
     return task.baseActivity?.packageName == sysUiPackageName
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
index 9d4b4bb..fe6066c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
@@ -55,6 +55,7 @@
 import com.android.wm.shell.compatui.api.CompatUIInfo;
 import com.android.wm.shell.compatui.impl.CompatUIEvents.SizeCompatRestartButtonClicked;
 import com.android.wm.shell.desktopmode.DesktopUserRepositories;
+import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
 import com.android.wm.shell.sysui.KeyguardChangeListener;
 import com.android.wm.shell.sysui.ShellController;
 import com.android.wm.shell.sysui.ShellInit;
@@ -70,7 +71,6 @@
 import java.util.Set;
 import java.util.function.Consumer;
 import java.util.function.Function;
-import java.util.function.IntPredicate;
 import java.util.function.Predicate;
 
 /**
@@ -667,9 +667,10 @@
 
     private void createOrUpdateUserAspectRatioSettingsLayout(@NonNull TaskInfo taskInfo,
             @Nullable ShellTaskOrganizer.TaskListener taskListener) {
+        boolean overridesShowAppHandle = DesktopModeStatus.overridesShowAppHandle(mContext);
         if (mUserAspectRatioSettingsLayout != null) {
             if (mUserAspectRatioSettingsLayout.needsToBeRecreated(taskInfo, taskListener)
-                    || mIsInDesktopMode) {
+                    || mIsInDesktopMode || overridesShowAppHandle) {
                 mUserAspectRatioSettingsLayout.release();
                 mUserAspectRatioSettingsLayout = null;
             } else {
@@ -682,8 +683,9 @@
                 return;
             }
         }
-        if (mIsInDesktopMode) {
-            // Return if in desktop mode.
+        if (mIsInDesktopMode || overridesShowAppHandle) {
+            // Return if in desktop mode or app handle menu is already showing change aspect ratio
+            // option.
             return;
         }
         // Create a new UI layout.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java
index aebd94f..5b6b897 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java
@@ -34,6 +34,7 @@
 import com.android.wm.shell.dagger.pip.TvPipModule;
 import com.android.wm.shell.recents.RecentTasksController;
 import com.android.wm.shell.shared.TransactionPool;
+import com.android.wm.shell.shared.annotations.ShellBackgroundThread;
 import com.android.wm.shell.shared.annotations.ShellMainThread;
 import com.android.wm.shell.splitscreen.SplitScreenController;
 import com.android.wm.shell.splitscreen.tv.TvSplitScreenController;
@@ -93,11 +94,12 @@
             SplitState splitState,
             @ShellMainThread ShellExecutor mainExecutor,
             Handler mainHandler,
+            @ShellBackgroundThread ShellExecutor bgExecutor,
             SystemWindows systemWindows) {
         return new TvSplitScreenController(context, shellInit, shellCommandHandler, shellController,
                 shellTaskOrganizer, syncQueue, rootTDAOrganizer, displayController,
                 displayImeController, displayInsetsController, transitions, transactionPool,
                 iconProvider, recentTasks, launchAdjacentController, multiInstanceHelper,
-                splitState, mainExecutor, mainHandler, systemWindows);
+                splitState, mainExecutor, mainHandler, bgExecutor, systemWindows);
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index de86b22..6c805c8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -64,6 +64,7 @@
 import com.android.wm.shell.common.TaskStackListenerImpl;
 import com.android.wm.shell.common.pip.PhonePipKeepClearAlgorithm;
 import com.android.wm.shell.common.pip.PhoneSizeSpecSource;
+import com.android.wm.shell.common.pip.PipAppOpsListener;
 import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
 import com.android.wm.shell.common.pip.PipBoundsState;
 import com.android.wm.shell.common.pip.PipDisplayLayoutState;
@@ -531,6 +532,13 @@
                 pipKeepClearAlgorithm, pipDisplayLayoutState, sizeSpecSource);
     }
 
+    @WMSingleton
+    @Provides
+    static PipAppOpsListener providePipAppOpsListener(Context context,
+            @ShellMainThread ShellExecutor mainExecutor) {
+        return new PipAppOpsListener(context, mainExecutor);
+    }
+
     //
     // Bubbles (optional feature)
     //
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 0cd0f4a..ace7f07 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
@@ -151,6 +151,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.common.WindowDecorTaskResourceLoader;
 import com.android.wm.shell.windowdecor.common.viewhost.DefaultWindowDecorViewHostSupplier;
 import com.android.wm.shell.windowdecor.common.viewhost.PooledWindowDecorViewHostSupplier;
 import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHost;
@@ -517,7 +518,8 @@
             MultiInstanceHelper multiInstanceHelper,
             SplitState splitState,
             @ShellMainThread ShellExecutor mainExecutor,
-            @ShellMainThread Handler mainHandler) {
+            @ShellMainThread Handler mainHandler,
+            @ShellBackgroundThread ShellExecutor bgExecutor) {
         return new SplitScreenController(
                 context,
                 shellInit,
@@ -541,7 +543,8 @@
                 multiInstanceHelper,
                 splitState,
                 mainExecutor,
-                mainHandler);
+                mainHandler,
+                bgExecutor);
     }
 
     //
@@ -767,6 +770,8 @@
     @WMSingleton
     @Provides
     static DesktopTilingDecorViewModel provideDesktopTilingViewModel(Context context,
+            @ShellMainThread MainCoroutineDispatcher mainDispatcher,
+            @ShellBackgroundThread CoroutineScope bgScope,
             DisplayController displayController,
             RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
             SyncTransactionQueue syncQueue,
@@ -775,9 +780,12 @@
             ToggleResizeDesktopTaskTransitionHandler toggleResizeDesktopTaskTransitionHandler,
             ReturnToDragStartAnimator returnToDragStartAnimator,
             @DynamicOverride DesktopUserRepositories desktopUserRepositories,
-            DesktopModeEventLogger desktopModeEventLogger) {
+            DesktopModeEventLogger desktopModeEventLogger,
+            WindowDecorTaskResourceLoader windowDecorTaskResourceLoader) {
         return new DesktopTilingDecorViewModel(
                 context,
+                mainDispatcher,
+                bgScope,
                 displayController,
                 rootTaskDisplayAreaOrganizer,
                 syncQueue,
@@ -786,7 +794,8 @@
                 toggleResizeDesktopTaskTransitionHandler,
                 returnToDragStartAnimator,
                 desktopUserRepositories,
-                desktopModeEventLogger
+                desktopModeEventLogger,
+                windowDecorTaskResourceLoader
         );
     }
 
@@ -903,6 +912,8 @@
             @ShellMainThread ShellExecutor shellExecutor,
             @ShellMainThread Handler mainHandler,
             @ShellMainThread Choreographer mainChoreographer,
+            @ShellMainThread MainCoroutineDispatcher mainDispatcher,
+            @ShellBackgroundThread CoroutineScope bgScope,
             @ShellBackgroundThread ShellExecutor bgExecutor,
             ShellInit shellInit,
             ShellCommandHandler shellCommandHandler,
@@ -929,13 +940,15 @@
             Optional<DesktopActivityOrientationChangeHandler> activityOrientationChangeHandler,
             FocusTransitionObserver focusTransitionObserver,
             DesktopModeEventLogger desktopModeEventLogger,
-            DesktopModeUiEventLogger desktopModeUiEventLogger
+            DesktopModeUiEventLogger desktopModeUiEventLogger,
+            WindowDecorTaskResourceLoader taskResourceLoader
     ) {
         if (!DesktopModeStatus.canEnterDesktopModeOrShowAppHandle(context)) {
             return Optional.empty();
         }
         return Optional.of(new DesktopModeWindowDecorViewModel(context, shellExecutor, mainHandler,
-                mainChoreographer, bgExecutor, shellInit, shellCommandHandler, windowManager,
+                mainChoreographer, mainDispatcher, bgScope, bgExecutor,
+                shellInit, shellCommandHandler, windowManager,
                 taskOrganizer, desktopUserRepositories, displayController, shellController,
                 displayInsetsController, syncQueue, transitions, desktopTasksController,
                 desktopImmersiveController.get(),
@@ -943,7 +956,18 @@
                 assistContentRequester, windowDecorViewHostSupplier, multiInstanceHelper,
                 desktopTasksLimiter, appHandleEducationController, appToWebEducationController,
                 windowDecorCaptionHandleRepository, activityOrientationChangeHandler,
-                focusTransitionObserver, desktopModeEventLogger, desktopModeUiEventLogger));
+                focusTransitionObserver, desktopModeEventLogger, desktopModeUiEventLogger,
+                taskResourceLoader));
+    }
+
+    @WMSingleton
+    @Provides
+    static WindowDecorTaskResourceLoader provideWindowDecorTaskResourceLoader(
+            @NonNull Context context, @NonNull ShellInit shellInit,
+            @NonNull ShellController shellController,
+            @NonNull ShellCommandHandler shellCommandHandler) {
+        return new WindowDecorTaskResourceLoader(context, shellInit, shellController,
+                shellCommandHandler);
     }
 
     @WMSingleton
@@ -1025,12 +1049,14 @@
     static DesktopUserRepositories provideDesktopUserRepositories(
             Context context,
             ShellInit shellInit,
+            ShellController shellController,
             DesktopPersistentRepository desktopPersistentRepository,
             DesktopRepositoryInitializer desktopRepositoryInitializer,
             @ShellMainThread CoroutineScope mainScope,
             UserManager userManager
     ) {
-        return new DesktopUserRepositories(context, shellInit, desktopPersistentRepository,
+        return new DesktopUserRepositories(context, shellInit, shellController,
+                desktopPersistentRepository,
                 desktopRepositoryInitializer,
                 mainScope, userManager);
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1Module.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1Module.java
index cfdfe3d..64b6c0f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1Module.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1Module.java
@@ -203,14 +203,6 @@
 
     @WMSingleton
     @Provides
-    static PipAppOpsListener providePipAppOpsListener(Context context,
-            PipTouchHandler pipTouchHandler,
-            @ShellMainThread ShellExecutor mainExecutor) {
-        return new PipAppOpsListener(context, pipTouchHandler.getMotionHelper(), mainExecutor);
-    }
-
-    @WMSingleton
-    @Provides
     static PipMotionHelper providePipMotionHelper(Context context,
             @ShellMainThread ShellExecutor mainExecutor,
             PipBoundsState pipBoundsState, PipTaskOrganizer pipTaskOrganizer,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java
index 3a99619..94a6e58 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java
@@ -28,6 +28,7 @@
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.SystemWindows;
 import com.android.wm.shell.common.TaskStackListenerImpl;
+import com.android.wm.shell.common.pip.PipAppOpsListener;
 import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
 import com.android.wm.shell.common.pip.PipBoundsState;
 import com.android.wm.shell.common.pip.PipDisplayLayoutState;
@@ -113,6 +114,8 @@
             ShellTaskOrganizer shellTaskOrganizer,
             PipTransitionState pipTransitionState,
             PipTouchHandler pipTouchHandler,
+            PipAppOpsListener pipAppOpsListener,
+            PhonePipMenuController pipMenuController,
             @ShellMainThread ShellExecutor mainExecutor) {
         if (!PipUtils.isPip2ExperimentEnabled()) {
             return Optional.empty();
@@ -121,7 +124,8 @@
                     context, shellInit, shellCommandHandler, shellController, displayController,
                     displayInsetsController, pipBoundsState, pipBoundsAlgorithm,
                     pipDisplayLayoutState, pipScheduler, taskStackListener, shellTaskOrganizer,
-                    pipTransitionState, pipTouchHandler, mainExecutor));
+                    pipTransitionState, pipTouchHandler, pipAppOpsListener, pipMenuController,
+                    mainExecutor));
         }
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/TvPipModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/TvPipModule.java
index 78e676f..b9f5482 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/TvPipModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/TvPipModule.java
@@ -233,12 +233,4 @@
     static PipParamsChangedForwarder providePipParamsChangedForwarder() {
         return new PipParamsChangedForwarder();
     }
-
-    @WMSingleton
-    @Provides
-    static PipAppOpsListener providePipAppOpsListener(Context context,
-            PipTaskOrganizer pipTaskOrganizer,
-            @ShellMainThread ShellExecutor mainExecutor) {
-        return new PipAppOpsListener(context, pipTaskOrganizer::removePip, mainExecutor);
-    }
 }
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 50187d5..d404634 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
@@ -239,7 +239,6 @@
             pending.minimizingTask?.let { minimizingTask -> findTaskChange(info, minimizingTask) }
         val launchChange = findDesktopTaskLaunchChange(info, pending.launchingTask)
         if (launchChange == null) {
-            check(minimizeChange == null)
             check(immersiveExitChange == null)
             logV("No launch Change, returning")
             return false
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 bccb609..c5b570dd 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
@@ -54,7 +54,9 @@
      * @property closingTasks task ids for tasks that are going to close, but are currently visible.
      * @property freeformTasksInZOrder list of current freeform task ids ordered from top to bottom
      * @property fullImmersiveTaskId the task id of the desktop task that is in full-immersive mode.
-     *   (top is at index 0).
+     * @property topTransparentFullscreenTaskId the task id of any current top transparent
+     *   fullscreen task launched on top of Desktop Mode. Cleared when the transparent task is
+     *   closed or sent to back. (top is at index 0).
      */
     private data class DesktopTaskData(
         val activeTasks: ArraySet<Int> = ArraySet(),
@@ -64,6 +66,7 @@
         val closingTasks: ArraySet<Int> = ArraySet(),
         val freeformTasksInZOrder: ArrayList<Int> = ArrayList(),
         var fullImmersiveTaskId: Int? = null,
+        var topTransparentFullscreenTaskId: Int? = null,
     ) {
         fun deepCopy(): DesktopTaskData =
             DesktopTaskData(
@@ -73,6 +76,7 @@
                 closingTasks = ArraySet(closingTasks),
                 freeformTasksInZOrder = ArrayList(freeformTasksInZOrder),
                 fullImmersiveTaskId = fullImmersiveTaskId,
+                topTransparentFullscreenTaskId = topTransparentFullscreenTaskId,
             )
 
         fun clear() {
@@ -82,6 +86,7 @@
             closingTasks.clear()
             freeformTasksInZOrder.clear()
             fullImmersiveTaskId = null
+            topTransparentFullscreenTaskId = null
         }
     }
 
@@ -220,11 +225,18 @@
     fun isMinimizedTask(taskId: Int) = desktopTaskDataSequence().any { taskId in it.minimizedTasks }
 
     /** Checks if a task is the only visible, non-closing, non-minimized task on its display. */
-    fun isOnlyVisibleNonClosingTask(taskId: Int): Boolean =
-        desktopTaskDataSequence().any {
+    fun isOnlyVisibleNonClosingTask(taskId: Int, displayId: Int = INVALID_DISPLAY): Boolean {
+        val seq =
+            if (displayId != INVALID_DISPLAY) {
+                sequenceOf(desktopTaskDataByDisplayId[displayId]).filterNotNull()
+            } else {
+                desktopTaskDataSequence()
+            }
+        return seq.any {
             it.visibleTasks.subtract(it.closingTasks).subtract(it.minimizedTasks).singleOrNull() ==
                 taskId
         }
+    }
 
     fun getActiveTasks(displayId: Int): ArraySet<Int> =
         ArraySet(desktopTaskDataByDisplayId[displayId]?.activeTasks)
@@ -315,13 +327,27 @@
     fun getTaskInFullImmersiveState(displayId: Int): Int? =
         desktopTaskDataByDisplayId.getOrCreate(displayId).fullImmersiveTaskId
 
+    /** Sets the top transparent fullscreen task id for a given display. */
+    fun setTopTransparentFullscreenTaskId(displayId: Int, taskId: Int) {
+        desktopTaskDataByDisplayId.getOrCreate(displayId).topTransparentFullscreenTaskId = taskId
+    }
+
+    /** Returns the top transparent fullscreen task id for a given display, or null. */
+    fun getTopTransparentFullscreenTaskId(displayId: Int): Int? =
+        desktopTaskDataByDisplayId.getOrCreate(displayId).topTransparentFullscreenTaskId
+
+    /** Clears the top transparent fullscreen task id info for a given display. */
+    fun clearTopTransparentFullscreenTaskId(displayId: Int) {
+        desktopTaskDataByDisplayId.getOrCreate(displayId).topTransparentFullscreenTaskId = null
+    }
+
     private fun notifyVisibleTaskListeners(displayId: Int, visibleTasksCount: Int) {
         visibleTasksListeners.forEach { (listener, executor) ->
             executor.execute { listener.onTasksVisibilityChanged(displayId, visibleTasksCount) }
         }
     }
 
-    /** Gets number of visible tasks on given [displayId] */
+    /** Gets number of visible freeform tasks on given [displayId] */
     fun getVisibleTaskCount(displayId: Int): Int =
         desktopTaskDataByDisplayId[displayId]?.visibleTasks?.size
             ?: 0.also { logD("getVisibleTaskCount=$it") }
@@ -519,6 +545,10 @@
             )
             pw.println("${innerPrefix}minimizedTasks=${data.minimizedTasks.toDumpString()}")
             pw.println("${innerPrefix}fullImmersiveTaskId=${data.fullImmersiveTaskId}")
+            pw.println(
+                "${innerPrefix}topTransparentFullscreenTaskId=" +
+                    "${data.topTransparentFullscreenTaskId}"
+            )
             pw.println("${innerPrefix}wallpaperActivityToken=$wallpaperActivityToken")
         }
     }
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 9a1abd5..a3d3a90 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
@@ -21,7 +21,6 @@
 import android.app.ActivityOptions
 import android.app.KeyguardManager
 import android.app.PendingIntent
-import android.app.TaskInfo
 import android.app.WindowConfiguration.ACTIVITY_TYPE_HOME
 import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD
 import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
@@ -84,6 +83,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.compatui.isTransparentTask
 import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.InputMethod
 import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ResizeTrigger
 import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger.DesktopUiEventEnum
@@ -308,11 +308,23 @@
         }
     }
 
-    /** Gets number of visible tasks in [displayId]. */
+    /** Gets number of visible freeform tasks in [displayId]. */
     fun visibleTaskCount(displayId: Int): Int = taskRepository.getVisibleTaskCount(displayId)
 
-    /** Returns true if any tasks are visible in Desktop Mode. */
-    fun isDesktopModeShowing(displayId: Int): Boolean = visibleTaskCount(displayId) > 0
+    /**
+     * Returns true if any freeform tasks are visible or if a transparent fullscreen task exists on
+     * top in Desktop Mode.
+     */
+    fun isDesktopModeShowing(displayId: Int): Boolean {
+        if (
+            DesktopModeFlags.INCLUDE_TOP_TRANSPARENT_FULLSCREEN_TASK_IN_DESKTOP_HEURISTIC
+                .isTrue() && DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_MODALS_POLICY.isTrue()
+        ) {
+            return visibleTaskCount(displayId) > 0 ||
+                taskRepository.getTopTransparentFullscreenTaskId(displayId) != null
+        }
+        return visibleTaskCount(displayId) > 0
+    }
 
     /** Moves focused task to desktop mode for given [displayId]. */
     fun moveFocusedTaskToDesktop(displayId: Int, transitionSource: DesktopModeTransitionSource) {
@@ -571,7 +583,7 @@
     ): ((IBinder) -> Unit)? {
         val taskId = taskInfo.taskId
         desktopTilingDecorViewModel.removeTaskIfTiled(displayId, taskId)
-        performDesktopExitCleanupIfNeeded(taskId, wct)
+        performDesktopExitCleanupIfNeeded(taskId, displayId, wct)
         taskRepository.addClosingTask(displayId, taskId)
         taskbarDesktopTaskListener?.onTaskbarCornerRoundingUpdate(
             doesAnyTaskRequireTaskbarRounding(displayId, taskId)
@@ -592,10 +604,15 @@
         val isMinimizingToPip = taskInfo.pictureInPictureParams?.isAutoEnterEnabled() ?: false
         // If task is going to PiP, start a PiP transition instead of a minimize transition
         if (isMinimizingToPip) {
-            val requestInfo = TransitionRequestInfo(
-                TRANSIT_PIP, /* triggerTask= */ null, taskInfo, /* remoteTransition= */ null,
-                /* displayChange= */ null, /* flags= */ 0
-            )
+            val requestInfo =
+                TransitionRequestInfo(
+                    TRANSIT_PIP,
+                    /* triggerTask= */ null,
+                    taskInfo,
+                    /* remoteTransition= */ null,
+                    /* displayChange= */ null,
+                    /* flags= */ 0,
+                )
             val requestRes = transitions.dispatchRequest(Binder(), requestInfo, /* skip= */ null)
             wct.merge(requestRes.second, true)
             pendingPipTransitionAndTask =
@@ -610,7 +627,7 @@
         val taskId = taskInfo.taskId
         val displayId = taskInfo.displayId
         val wct = WindowContainerTransaction()
-        performDesktopExitCleanupIfNeeded(taskId, wct)
+        performDesktopExitCleanupIfNeeded(taskId, displayId, wct)
         // Notify immersive handler as it might need to exit immersive state.
         val exitResult =
             desktopImmersiveController.exitImmersiveIfApplicable(
@@ -759,15 +776,12 @@
         displayId: Int = DEFAULT_DISPLAY,
     ): IBinder {
         val taskIdToMinimize =
-            if (launchingTaskId != null) {
-                addAndGetMinimizeChanges(displayId, wct, newTaskId = launchingTaskId)
-            } else {
-                logW("Starting desktop task launch without checking the task-limit")
-                // TODO(b/378920066): This currently does not respect the desktop window limit.
-                //  It's possible that |launchingTaskId| is null when launching using an intent, and
-                //  the task-limit should be respected then too.
-                null
-            }
+            addAndGetMinimizeChanges(
+                displayId,
+                wct,
+                newTaskId = launchingTaskId,
+                launchingNewIntent = launchingTaskId == null,
+            )
         val exitImmersiveResult =
             desktopImmersiveController.exitImmersiveIfApplicable(
                 wct = wct,
@@ -861,9 +875,17 @@
         }
 
         val wct = WindowContainerTransaction()
-        if (!task.isFreeform) addMoveToDesktopChanges(wct, task, displayId)
+        if (!task.isFreeform) {
+            addMoveToDesktopChanges(wct, task, displayId)
+        } else if (Flags.enableMoveToNextDisplayShortcut()) {
+            applyFreeformDisplayChange(wct, task, displayId)
+        }
         wct.reparent(task.token, displayAreaInfo.token, true /* onTop */)
 
+        if (Flags.enablePerDisplayDesktopWallpaperActivity()) {
+            performDesktopExitCleanupIfNeeded(task.taskId, task.displayId, wct)
+        }
+
         transitions.startTransition(TRANSIT_CHANGE, wct, null /* handler */)
     }
 
@@ -1348,9 +1370,22 @@
      * Remove wallpaper activity if task provided is last task and wallpaper activity token is not
      * null
      */
-    private fun performDesktopExitCleanupIfNeeded(taskId: Int, wct: WindowContainerTransaction) {
-        if (!taskRepository.isOnlyVisibleNonClosingTask(taskId)) {
-            return
+    private fun performDesktopExitCleanupIfNeeded(
+        taskId: Int,
+        displayId: Int,
+        wct: WindowContainerTransaction,
+    ) {
+        if (Flags.enablePerDisplayDesktopWallpaperActivity()) {
+            if (!taskRepository.isOnlyVisibleNonClosingTask(taskId, displayId)) {
+                return
+            }
+            if (displayId != DEFAULT_DISPLAY) {
+                return
+            }
+        } else {
+            if (!taskRepository.isOnlyVisibleNonClosingTask(taskId)) {
+                return
+            }
         }
         desktopModeEnterExitTransitionListener?.onExitDesktopModeTransitionStarted(
             FULLSCREEN_ANIMATION_DURATION
@@ -1392,7 +1427,7 @@
     override fun onTransitionConsumed(
         transition: IBinder,
         aborted: Boolean,
-        finishT: Transaction?
+        finishT: Transaction?,
     ) {
         pendingPipTransitionAndTask?.let { (pipTransition, taskId) ->
             if (transition == pipTransition) {
@@ -1573,7 +1608,7 @@
             TransitionUtil.isOpeningType(request.type) &&
             taskRepository.isActiveTask(triggerTask.taskId))
 
-    private fun isIncompatibleTask(task: TaskInfo) =
+    private fun isIncompatibleTask(task: RunningTaskInfo) =
         DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_MODALS_POLICY.isTrue() &&
             isTopActivityExemptFromDesktopWindowing(context, task)
 
@@ -1829,6 +1864,15 @@
      * fullscreen.
      */
     private fun handleIncompatibleTaskLaunch(task: RunningTaskInfo): WindowContainerTransaction? {
+        logV("handleIncompatibleTaskLaunch")
+        if (!isDesktopModeShowing(task.displayId)) return null
+        // Only update task repository for transparent task.
+        if (
+            DesktopModeFlags.INCLUDE_TOP_TRANSPARENT_FULLSCREEN_TASK_IN_DESKTOP_HEURISTIC
+                .isTrue() && isTransparentTask(task)
+        ) {
+            taskRepository.setTopTransparentFullscreenTaskId(task.displayId, task.taskId)
+        }
         // Already fullscreen, no-op.
         if (task.isFullscreen) return null
         return WindowContainerTransaction().also { wct -> addMoveToFullscreenChanges(wct, task) }
@@ -1844,7 +1888,7 @@
         if (!isDesktopModeShowing(task.displayId)) return null
 
         val wct = WindowContainerTransaction()
-        performDesktopExitCleanupIfNeeded(task.taskId, wct)
+        performDesktopExitCleanupIfNeeded(task.taskId, task.displayId, wct)
 
         if (!DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION.isTrue()) {
             taskRepository.addClosingTask(task.displayId, task.taskId)
@@ -1890,6 +1934,50 @@
         }
     }
 
+    /**
+     * Apply changes to move a freeform task from one display to another, which includes handling
+     * density changes between displays.
+     */
+    private fun applyFreeformDisplayChange(
+        wct: WindowContainerTransaction,
+        taskInfo: RunningTaskInfo,
+        destDisplayId: Int,
+    ) {
+        val sourceLayout = displayController.getDisplayLayout(taskInfo.displayId) ?: return
+        val destLayout = displayController.getDisplayLayout(destDisplayId) ?: return
+        val bounds = taskInfo.configuration.windowConfiguration.bounds
+        val scaledWidth = bounds.width() * destLayout.densityDpi() / sourceLayout.densityDpi()
+        val scaledHeight = bounds.height() * destLayout.densityDpi() / sourceLayout.densityDpi()
+        val sourceWidthMargin = sourceLayout.width() - bounds.width()
+        val sourceHeightMargin = sourceLayout.height() - bounds.height()
+        val destWidthMargin = destLayout.width() - scaledWidth
+        val destHeightMargin = destLayout.height() - scaledHeight
+        val scaledLeft =
+            if (sourceWidthMargin != 0) {
+                bounds.left * destWidthMargin / sourceWidthMargin
+            } else {
+                destWidthMargin / 2
+            }
+        val scaledTop =
+            if (sourceHeightMargin != 0) {
+                bounds.top * destHeightMargin / sourceHeightMargin
+            } else {
+                destHeightMargin / 2
+            }
+        val boundsWithinDisplay =
+            if (destWidthMargin >= 0 && destHeightMargin >= 0) {
+                Rect(0, 0, scaledWidth, scaledHeight).apply {
+                    offsetTo(
+                        scaledLeft.coerceIn(0, destWidthMargin),
+                        scaledTop.coerceIn(0, destHeightMargin),
+                    )
+                }
+            } else {
+                getInitialBounds(destLayout, taskInfo, destDisplayId)
+            }
+        wct.setBounds(taskInfo.token, boundsWithinDisplay)
+    }
+
     private fun getInitialBounds(
         displayLayout: DisplayLayout,
         taskInfo: RunningTaskInfo,
@@ -1927,7 +2015,7 @@
             wct.setDensityDpi(taskInfo.token, getDefaultDensityDpi())
         }
 
-        performDesktopExitCleanupIfNeeded(taskInfo.taskId, wct)
+        performDesktopExitCleanupIfNeeded(taskInfo.taskId, taskInfo.displayId, wct)
     }
 
     private fun cascadeWindow(bounds: Rect, displayLayout: DisplayLayout, displayId: Int) {
@@ -1961,17 +2049,21 @@
         // want it overridden in multi-window.
         wct.setDensityDpi(taskInfo.token, getDefaultDensityDpi())
 
-        performDesktopExitCleanupIfNeeded(taskInfo.taskId, wct)
+        performDesktopExitCleanupIfNeeded(taskInfo.taskId, taskInfo.displayId, wct)
     }
 
     /** Returns the ID of the Task that will be minimized, or null if no task will be minimized. */
     private fun addAndGetMinimizeChanges(
         displayId: Int,
         wct: WindowContainerTransaction,
-        newTaskId: Int,
+        newTaskId: Int?,
+        launchingNewIntent: Boolean = false,
     ): Int? {
         if (!desktopTasksLimiter.isPresent) return null
-        return desktopTasksLimiter.get().addAndGetMinimizeTaskChanges(displayId, wct, newTaskId)
+        require(newTaskId == null || !launchingNewIntent)
+        return desktopTasksLimiter
+            .get()
+            .addAndGetMinimizeTaskChanges(displayId, wct, newTaskId, launchingNewIntent)
     }
 
     private fun addPendingMinimizeTransition(transition: IBinder, taskIdToMinimize: Int) {
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 635078e..45faba6 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
@@ -214,12 +214,17 @@
     fun addAndGetMinimizeTaskChanges(
         displayId: Int,
         wct: WindowContainerTransaction,
-        newFrontTaskId: Int,
+        newFrontTaskId: Int?,
+        launchingNewIntent: Boolean = false,
     ): Int? {
         logV("addAndGetMinimizeTaskChanges, newFrontTask=%d", newFrontTaskId)
         val taskRepository = desktopUserRepositories.current
         val taskIdToMinimize =
-            getTaskIdToMinimize(taskRepository.getExpandedTasksOrdered(displayId), newFrontTaskId)
+            getTaskIdToMinimize(
+                taskRepository.getExpandedTasksOrdered(displayId),
+                newFrontTaskId,
+                launchingNewIntent,
+            )
         // If it's a running task, reorder it to back.
         taskIdToMinimize
             ?.let { shellTaskOrganizer.getRunningTaskInfo(it) }
@@ -242,15 +247,24 @@
      * Returns the minimized task from the list of visible tasks ordered from front to back with the
      * new task placed in front of other tasks.
      */
-    fun getTaskIdToMinimize(visibleOrderedTasks: List<Int>, newTaskIdInFront: Int? = null): Int? {
+    fun getTaskIdToMinimize(
+        visibleOrderedTasks: List<Int>,
+        newTaskIdInFront: Int? = null,
+        launchingNewIntent: Boolean = false,
+    ): Int? {
         return getTaskIdToMinimize(
-            createOrderedTaskListWithGivenTaskInFront(visibleOrderedTasks, newTaskIdInFront)
+            createOrderedTaskListWithGivenTaskInFront(visibleOrderedTasks, newTaskIdInFront),
+            launchingNewIntent,
         )
     }
 
     /** Returns the Task to minimize given a list of visible tasks ordered from front to back. */
-    private fun getTaskIdToMinimize(visibleOrderedTasks: List<Int>): Int? {
-        if (visibleOrderedTasks.size <= maxTasksLimit) {
+    private fun getTaskIdToMinimize(
+        visibleOrderedTasks: List<Int>,
+        launchingNewIntent: Boolean,
+    ): Int? {
+        val newTasksOpening = if (launchingNewIntent) 1 else 0
+        if (visibleOrderedTasks.size + newTasksOpening <= maxTasksLimit) {
             logV("No need to minimize; tasks below limit")
             // No need to minimize anything
             return null
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt
index 9625b71..5c79658 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt
@@ -75,6 +75,12 @@
         finishTransaction: SurfaceControl.Transaction,
     ) {
         // TODO: b/332682201 Update repository state
+        if (
+            DesktopModeFlags.INCLUDE_TOP_TRANSPARENT_FULLSCREEN_TASK_IN_DESKTOP_HEURISTIC
+                .isTrue() && DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_MODALS_POLICY.isTrue()
+        ) {
+            updateTopTransparentFullscreenTaskId(info)
+        }
         updateWallpaperToken(info)
         if (DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION.isTrue()) {
             handleBackNavigation(transition, info)
@@ -264,4 +270,22 @@
             }
         }
     }
+
+    private fun updateTopTransparentFullscreenTaskId(info: TransitionInfo) {
+        info.changes.forEach { change ->
+            change.taskInfo?.let { task ->
+                val desktopRepository = desktopUserRepositories.getProfile(task.userId)
+                val displayId = task.displayId
+                // Clear `topTransparentFullscreenTask` information from repository if task
+                // is closed or sent to back.
+                if (
+                    TransitionUtil.isClosingMode(change.mode) &&
+                        task.taskId ==
+                            desktopRepository.getTopTransparentFullscreenTaskId(displayId)
+                ) {
+                    desktopRepository.clearTopTransparentFullscreenTaskId(displayId)
+                }
+            }
+        }
+    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopUserRepositories.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopUserRepositories.kt
index e5f5283..8b5d1c5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopUserRepositories.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopUserRepositories.kt
@@ -28,6 +28,7 @@
 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
+import com.android.wm.shell.sysui.ShellController
 import com.android.wm.shell.sysui.ShellInit
 import com.android.wm.shell.sysui.UserChangeListener
 import kotlinx.coroutines.CoroutineScope
@@ -36,6 +37,7 @@
 class DesktopUserRepositories(
     context: Context,
     shellInit: ShellInit,
+    private val shellController: ShellController,
     private val persistentRepository: DesktopPersistentRepository,
     private val repositoryInitializer: DesktopRepositoryInitializer,
     @ShellMainThread private val mainCoroutineScope: CoroutineScope,
@@ -61,15 +63,16 @@
     init {
         userId = ActivityManager.getCurrentUser()
         if (DesktopModeStatus.canEnterDesktopMode(context)) {
-            shellInit.addInitCallback(::initRepoFromPersistentStorage, this)
+            shellInit.addInitCallback(::onInit, this)
         }
         if (Flags.enableDesktopWindowingHsum()) {
             userIdToProfileIdsMap[userId] = userManager.getProfiles(userId).map { it.id }
         }
     }
 
-    private fun initRepoFromPersistentStorage() {
+    private fun onInit() {
         repositoryInitializer.initialize(this)
+        shellController.addUserChangeListener(this)
     }
 
     /** Returns [DesktopRepository] for the parent user id. */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index 582df48..9c3e815 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -562,6 +562,8 @@
             e.printStackTrace();
         }
 
+        mAppOpsListener.setCallback(mTouchHandler.getMotionHelper());
+
         // Handle for system task stack changes.
         mTaskStackListener.addListener(
                 new TaskStackListenerCallback() {
@@ -779,6 +781,7 @@
                 // cancel any running animator, as it is using stale display layout information
                 animator.cancel();
             }
+            mMenuController.hideMenu();
             onDisplayChangedUncheck(layout, saveRestoreSnapFraction);
         }
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
index 8ac7f89..56a158b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
@@ -269,6 +269,8 @@
 
         mShellController.addConfigurationChangeListener(this);
         mShellController.addUserChangeListener(this);
+
+        mAppOpsListener.setCallback(mPipTaskOrganizer::removePip);
     }
 
     @Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java
index e309da1..8c6d5f5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java
@@ -23,6 +23,7 @@
 import android.annotation.NonNull;
 import android.app.ActivityManager;
 import android.app.PictureInPictureParams;
+import android.app.TaskInfo;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.ActivityInfo;
@@ -54,6 +55,7 @@
 import com.android.wm.shell.common.TaskStackListenerImpl;
 import com.android.wm.shell.common.pip.IPip;
 import com.android.wm.shell.common.pip.IPipAnimationListener;
+import com.android.wm.shell.common.pip.PipAppOpsListener;
 import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
 import com.android.wm.shell.common.pip.PipBoundsState;
 import com.android.wm.shell.common.pip.PipDisplayLayoutState;
@@ -94,6 +96,8 @@
     private final ShellTaskOrganizer mShellTaskOrganizer;
     private final PipTransitionState mPipTransitionState;
     private final PipTouchHandler mPipTouchHandler;
+    private final PipAppOpsListener mPipAppOpsListener;
+    private final PhonePipMenuController mPipMenuController;
     private final ShellExecutor mMainExecutor;
     private final PipImpl mImpl;
     private final List<Consumer<Boolean>> mOnIsInPipStateChangedListeners = new ArrayList<>();
@@ -137,6 +141,8 @@
             ShellTaskOrganizer shellTaskOrganizer,
             PipTransitionState pipTransitionState,
             PipTouchHandler pipTouchHandler,
+            PipAppOpsListener pipAppOpsListener,
+            PhonePipMenuController pipMenuController,
             ShellExecutor mainExecutor) {
         mContext = context;
         mShellCommandHandler = shellCommandHandler;
@@ -152,6 +158,8 @@
         mPipTransitionState = pipTransitionState;
         mPipTransitionState.addPipTransitionStateChangedListener(this);
         mPipTouchHandler = pipTouchHandler;
+        mPipAppOpsListener = pipAppOpsListener;
+        mPipMenuController = pipMenuController;
         mMainExecutor = mainExecutor;
         mImpl = new PipImpl();
 
@@ -177,6 +185,8 @@
             ShellTaskOrganizer shellTaskOrganizer,
             PipTransitionState pipTransitionState,
             PipTouchHandler pipTouchHandler,
+            PipAppOpsListener pipAppOpsListener,
+            PhonePipMenuController pipMenuController,
             ShellExecutor mainExecutor) {
         if (!context.getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)) {
             ProtoLog.w(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
@@ -186,7 +196,8 @@
         return new PipController(context, shellInit, shellCommandHandler, shellController,
                 displayController, displayInsetsController, pipBoundsState, pipBoundsAlgorithm,
                 pipDisplayLayoutState, pipScheduler, taskStackListener, shellTaskOrganizer,
-                pipTransitionState, pipTouchHandler, mainExecutor);
+                pipTransitionState, pipTouchHandler, pipAppOpsListener, pipMenuController,
+                mainExecutor);
     }
 
     public PipImpl getPipImpl() {
@@ -225,6 +236,20 @@
                 mPipScheduler.scheduleExitPipViaExpand();
             }
         });
+
+        mPipAppOpsListener.setCallback(mPipTouchHandler.getMotionHelper());
+        mPipTransitionState.addPipTransitionStateChangedListener(
+                (oldState, newState, extra) -> {
+                    if (newState == PipTransitionState.ENTERED_PIP) {
+                        final TaskInfo taskInfo = mPipTransitionState.getPipTaskInfo();
+                        if (taskInfo != null && taskInfo.topActivity != null) {
+                            mPipAppOpsListener.onActivityPinned(
+                                    taskInfo.topActivity.getPackageName());
+                        }
+                    } else if (newState == PipTransitionState.EXITED_PIP) {
+                        mPipAppOpsListener.onActivityUnpinned();
+                    }
+                });
     }
 
     private ExternalInterfaceBinder createExternalInterface() {
@@ -309,6 +334,7 @@
         }
 
         mPipTouchHandler.updateMinMaxSize(mPipBoundsState.getAspectRatio());
+        mPipMenuController.hideMenu();
 
         if (mPipTransitionState.isInFixedRotation()) {
             // Do not change the bounds when in fixed rotation, but do update the movement bounds
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 dae3c21..acb5622b 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
@@ -124,7 +124,6 @@
     // Internal state and relevant cached info
     //
 
-    @Nullable
     private Transitions.TransitionFinishCallback mFinishCallback;
 
     private ValueAnimator mTransitionAnimator;
@@ -236,7 +235,6 @@
             @NonNull SurfaceControl.Transaction startTransaction,
             @NonNull SurfaceControl.Transaction finishTransaction,
             @NonNull Transitions.TransitionFinishCallback finishCallback) {
-        mFinishCallback = finishCallback;
         if (transition == mEnterTransition || info.getType() == TRANSIT_PIP) {
             mEnterTransition = null;
             // If we are in swipe PiP to Home transition we are ENTERING_PIP as a jumpcut transition
@@ -282,7 +280,6 @@
         if (isRemovePipTransition(info)) {
             return removePipImmediately(info, startTransaction, finishTransaction, finishCallback);
         }
-        mFinishCallback = null;
         return false;
     }
 
@@ -331,6 +328,7 @@
         if (pipChange == null) {
             return false;
         }
+        mFinishCallback = finishCallback;
         // 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,
@@ -378,6 +376,7 @@
         if (pipActivityChange == null) {
             return false;
         }
+        mFinishCallback = finishCallback;
 
         final SurfaceControl pipLeash = getLeash(pipChange);
         final Rect destinationBounds = pipChange.getEndAbsBounds();
@@ -446,6 +445,7 @@
         if (pipActivityChange == null) {
             return false;
         }
+        mFinishCallback = finishCallback;
 
         final SurfaceControl pipLeash = getLeash(pipChange);
         final Rect startBounds = pipChange.getStartAbsBounds();
@@ -572,6 +572,7 @@
         if (pipChange == null) {
             return false;
         }
+        mFinishCallback = finishCallback;
 
         Rect destinationBounds = pipChange.getEndAbsBounds();
         SurfaceControl pipLeash = mPipTransitionState.getPinnedTaskLeash();
@@ -614,6 +615,7 @@
                 return false;
             }
         }
+        mFinishCallback = finishCallback;
 
         // The parent change if we were in a multi-activity PiP; null if single activity PiP.
         final TransitionInfo.Change parentBeforePip = pipChange.getTaskInfo() == null
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index 2998a07..39ed206 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -185,6 +185,7 @@
     private final LauncherApps mLauncherApps;
     private final RootTaskDisplayAreaOrganizer mRootTDAOrganizer;
     private final ShellExecutor mMainExecutor;
+    private final ShellExecutor mBgExecutor;
     private final Handler mMainHandler;
     private final SplitScreenImpl mImpl = new SplitScreenImpl();
     private final DisplayController mDisplayController;
@@ -231,7 +232,8 @@
             MultiInstanceHelper multiInstanceHelper,
             SplitState splitState,
             ShellExecutor mainExecutor,
-            Handler mainHandler) {
+            Handler mainHandler,
+            ShellExecutor bgExecutor) {
         mShellCommandHandler = shellCommandHandler;
         mShellController = shellController;
         mTaskOrganizer = shellTaskOrganizer;
@@ -241,6 +243,7 @@
         mRootTDAOrganizer = rootTDAOrganizer;
         mMainExecutor = mainExecutor;
         mMainHandler = mainHandler;
+        mBgExecutor = bgExecutor;
         mDisplayController = displayController;
         mDisplayImeController = displayImeController;
         mDisplayInsetsController = displayInsetsController;
@@ -298,8 +301,9 @@
         return new StageCoordinator(mContext, DEFAULT_DISPLAY, mSyncQueue,
                 mTaskOrganizer, mDisplayController, mDisplayImeController,
                 mDisplayInsetsController, mTransitions, mTransactionPool, mIconProvider,
-                mMainExecutor, mMainHandler, mRecentTasksOptional, mLaunchAdjacentController,
-                mWindowDecorViewModel, mSplitState);
+                mMainExecutor, mMainHandler, mBgExecutor, mRecentTasksOptional,
+                mLaunchAdjacentController, mWindowDecorViewModel, mSplitState,
+                mDesktopTasksController);
     }
 
     @Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index e93ca9e..246760e 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
@@ -20,6 +20,7 @@
 import static android.app.ActivityTaskManager.INVALID_TASK_ID;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.content.Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT;
@@ -141,10 +142,12 @@
 import com.android.wm.shell.common.split.SplitScreenUtils;
 import com.android.wm.shell.common.split.SplitState;
 import com.android.wm.shell.common.split.SplitWindowManager;
+import com.android.wm.shell.desktopmode.DesktopTasksController;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
 import com.android.wm.shell.recents.RecentTasksController;
 import com.android.wm.shell.shared.TransactionPool;
 import com.android.wm.shell.shared.TransitionUtil;
+import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
 import com.android.wm.shell.shared.split.SplitBounds;
 import com.android.wm.shell.shared.split.SplitScreenConstants.PersistentSnapPosition;
 import com.android.wm.shell.shared.split.SplitScreenConstants.SplitIndex;
@@ -213,12 +216,14 @@
     private final SplitscreenEventLogger mLogger;
     private final ShellExecutor mMainExecutor;
     private final Handler mMainHandler;
+    private final ShellExecutor mBgExecutor;
     // Cache live tile tasks while entering recents, evict them from stages in finish transaction
     // if user is opening another task(s).
     private final ArrayList<Integer> mPausingTasks = new ArrayList<>();
     private final Optional<RecentTasksController> mRecentTasks;
     private final LaunchAdjacentController mLaunchAdjacentController;
     private final Optional<WindowDecorViewModel> mWindowDecorViewModel;
+    private final Optional<DesktopTasksController> mDesktopTasksController;
     /** Singleton source of truth for the current state of split screen on this device. */
     private final SplitState mSplitState;
 
@@ -340,14 +345,23 @@
                 }
             };
 
-    protected StageCoordinator(Context context, int displayId, SyncTransactionQueue syncQueue,
-            ShellTaskOrganizer taskOrganizer, DisplayController displayController,
+    protected StageCoordinator(Context context,
+            int displayId,
+            SyncTransactionQueue syncQueue,
+            ShellTaskOrganizer taskOrganizer,
+            DisplayController displayController,
             DisplayImeController displayImeController,
-            DisplayInsetsController displayInsetsController, Transitions transitions,
-            TransactionPool transactionPool, IconProvider iconProvider, ShellExecutor mainExecutor,
-            Handler mainHandler, Optional<RecentTasksController> recentTasks,
+            DisplayInsetsController displayInsetsController,
+            Transitions transitions,
+            TransactionPool transactionPool,
+            IconProvider iconProvider,
+            ShellExecutor mainExecutor,
+            Handler mainHandler,
+            ShellExecutor bgExecutor,
+            Optional<RecentTasksController> recentTasks,
             LaunchAdjacentController launchAdjacentController,
-            Optional<WindowDecorViewModel> windowDecorViewModel, SplitState splitState) {
+            Optional<WindowDecorViewModel> windowDecorViewModel, SplitState splitState,
+            Optional<DesktopTasksController> desktopTasksController) {
         mContext = context;
         mDisplayId = displayId;
         mSyncQueue = syncQueue;
@@ -355,10 +369,12 @@
         mLogger = new SplitscreenEventLogger();
         mMainExecutor = mainExecutor;
         mMainHandler = mainHandler;
+        mBgExecutor = bgExecutor;
         mRecentTasks = recentTasks;
         mLaunchAdjacentController = launchAdjacentController;
         mWindowDecorViewModel = windowDecorViewModel;
         mSplitState = splitState;
+        mDesktopTasksController = desktopTasksController;
 
         taskOrganizer.createRootTask(displayId, WINDOWING_MODE_FULLSCREEN, this /* listener */);
 
@@ -370,6 +386,8 @@
                     this /*stageListenerCallbacks*/,
                     mSyncQueue,
                     iconProvider,
+                    mMainExecutor,
+                    mBgExecutor,
                     mWindowDecorViewModel);
         } else {
             mMainStage = new StageTaskListener(
@@ -379,6 +397,8 @@
                     this /*stageListenerCallbacks*/,
                     mSyncQueue,
                     iconProvider,
+                    mMainExecutor,
+                    mBgExecutor,
                     mWindowDecorViewModel, STAGE_TYPE_MAIN);
             mSideStage = new StageTaskListener(
                     mContext,
@@ -387,6 +407,8 @@
                     this /*stageListenerCallbacks*/,
                     mSyncQueue,
                     iconProvider,
+                    mMainExecutor,
+                    mBgExecutor,
                     mWindowDecorViewModel, STAGE_TYPE_SIDE);
         }
         mDisplayController = displayController;
@@ -408,15 +430,25 @@
     }
 
     @VisibleForTesting
-    StageCoordinator(Context context, int displayId, SyncTransactionQueue syncQueue,
-            ShellTaskOrganizer taskOrganizer, StageTaskListener mainStage,
-            StageTaskListener sideStage, DisplayController displayController,
+    StageCoordinator(Context context,
+            int displayId,
+            SyncTransactionQueue syncQueue,
+            ShellTaskOrganizer taskOrganizer,
+            StageTaskListener mainStage,
+            StageTaskListener sideStage,
+            DisplayController displayController,
             DisplayImeController displayImeController,
-            DisplayInsetsController displayInsetsController, SplitLayout splitLayout,
-            Transitions transitions, TransactionPool transactionPool, ShellExecutor mainExecutor,
-            Handler mainHandler, Optional<RecentTasksController> recentTasks,
+            DisplayInsetsController displayInsetsController,
+            SplitLayout splitLayout,
+            Transitions transitions,
+            TransactionPool transactionPool,
+            ShellExecutor mainExecutor,
+            Handler mainHandler,
+            ShellExecutor bgExecutor,
+            Optional<RecentTasksController> recentTasks,
             LaunchAdjacentController launchAdjacentController,
-            Optional<WindowDecorViewModel> windowDecorViewModel, SplitState splitState) {
+            Optional<WindowDecorViewModel> windowDecorViewModel, SplitState splitState,
+            Optional<DesktopTasksController> desktopTasksController) {
         mContext = context;
         mDisplayId = displayId;
         mSyncQueue = syncQueue;
@@ -433,10 +465,12 @@
         mLogger = new SplitscreenEventLogger();
         mMainExecutor = mainExecutor;
         mMainHandler = mainHandler;
+        mBgExecutor = bgExecutor;
         mRecentTasks = recentTasks;
         mLaunchAdjacentController = launchAdjacentController;
         mWindowDecorViewModel = windowDecorViewModel;
         mSplitState = splitState;
+        mDesktopTasksController = desktopTasksController;
 
         mDisplayController.addDisplayWindowListener(this);
         transitions.addHandler(this);
@@ -2740,9 +2774,17 @@
         final @WindowManager.TransitionType int type = request.getType();
         final boolean isOpening = isOpeningType(type);
         final boolean inFullscreen = triggerTask.getWindowingMode() == WINDOWING_MODE_FULLSCREEN;
+        final boolean inDesktopMode = mDesktopTasksController.isPresent()
+                && mDesktopTasksController.get().isDesktopModeShowing(mDisplayId);
+        final boolean isLaunchingDesktopTask = isOpening && DesktopModeStatus.canEnterDesktopMode(
+                mContext) && triggerTask.getWindowingMode() == WINDOWING_MODE_FREEFORM;
         final StageTaskListener stage = getStageOfTask(triggerTask);
 
-        if (isOpening && inFullscreen) {
+        if (inDesktopMode || isLaunchingDesktopTask) {
+            // Don't handle request when desktop mode is showing (since they don't coexist), or
+            // when launching a desktop task (defer to DesktopTasksController)
+            return null;
+        } else if (isOpening && inFullscreen) {
             // One task is opening into fullscreen mode, remove the corresponding split record.
             mRecentTasks.ifPresent(recentTasks -> recentTasks.removeSplitPair(triggerTask.taskId));
             logExit(EXIT_REASON_FULLSCREEN_REQUEST);
@@ -2963,7 +3005,8 @@
                     mSplitLayout.update(startTransaction, false /* resetImePosition */);
                 }
 
-                if (mMixedHandler.isEnteringPip(change, transitType)) {
+                if (mMixedHandler.isEnteringPip(change, transitType)
+                        && getSplitItemStage(change.getLastParent()) != STAGE_TYPE_UNDEFINED) {
                     pipChange = change;
                 }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageOrderOperator.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageOrderOperator.kt
index a921004..5256e78 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageOrderOperator.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageOrderOperator.kt
@@ -20,6 +20,7 @@
 import com.android.internal.protolog.ProtoLog
 import com.android.launcher3.icons.IconProvider
 import com.android.wm.shell.ShellTaskOrganizer
+import com.android.wm.shell.common.ShellExecutor
 import com.android.wm.shell.common.SyncTransactionQueue
 import com.android.wm.shell.protolog.ShellProtoLogGroup
 import com.android.wm.shell.shared.split.SplitScreenConstants
@@ -52,6 +53,8 @@
         stageCallbacks: StageTaskListener.StageListenerCallbacks,
         syncQueue: SyncTransactionQueue,
         iconProvider: IconProvider,
+        mainExecutor: ShellExecutor,
+        bgExecutor: ShellExecutor,
         windowDecorViewModel: Optional<WindowDecorViewModel>
     ) {
 
@@ -83,6 +86,8 @@
                 stageCallbacks,
                 syncQueue,
                 iconProvider,
+                mainExecutor,
+                bgExecutor,
                 windowDecorViewModel,
                 stageIds[i])
             )
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
index f1245ba..816f51f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
@@ -48,6 +48,7 @@
 import com.android.internal.util.ArrayUtils;
 import com.android.launcher3.icons.IconProvider;
 import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.SurfaceUtils;
 import com.android.wm.shell.common.SyncTransactionQueue;
 import com.android.wm.shell.common.split.SplitDecorManager;
@@ -95,6 +96,8 @@
     private final StageListenerCallbacks mCallbacks;
     private final SyncTransactionQueue mSyncQueue;
     private final IconProvider mIconProvider;
+    private final ShellExecutor mMainExecutor;
+    private final ShellExecutor mBgExecutor;
     private final Optional<WindowDecorViewModel> mWindowDecorViewModel;
 
     /** Whether or not the root task has been created. */
@@ -111,14 +114,21 @@
     // TODO(b/204308910): Extracts SplitDecorManager related code to common package.
     private SplitDecorManager mSplitDecorManager;
 
-    StageTaskListener(Context context, ShellTaskOrganizer taskOrganizer, int displayId,
-            StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue,
+    StageTaskListener(Context context,
+            ShellTaskOrganizer taskOrganizer,
+            int displayId,
+            StageListenerCallbacks callbacks,
+            SyncTransactionQueue syncQueue,
             IconProvider iconProvider,
+            ShellExecutor mainExecutor,
+            ShellExecutor bgExecutor,
             Optional<WindowDecorViewModel> windowDecorViewModel, int id) {
         mContext = context;
         mCallbacks = callbacks;
         mSyncQueue = syncQueue;
         mIconProvider = iconProvider;
+        mMainExecutor = mainExecutor;
+        mBgExecutor = bgExecutor;
         mWindowDecorViewModel = windowDecorViewModel;
         taskOrganizer.createRootTask(displayId, WINDOWING_MODE_MULTI_WINDOW, this);
         mId = id;
@@ -214,9 +224,8 @@
         if (mRootTaskInfo == null) {
             mRootLeash = leash;
             mRootTaskInfo = taskInfo;
-            mSplitDecorManager = new SplitDecorManager(
-                    mRootTaskInfo.configuration,
-                    mIconProvider);
+            mSplitDecorManager = new SplitDecorManager(mRootTaskInfo.configuration, mIconProvider,
+                    mMainExecutor, mBgExecutor);
             mHasRootTask = true;
             mCallbacks.onRootTaskAppeared();
             if (mVisible != mRootTaskInfo.isVisible) {
@@ -344,12 +353,6 @@
         }
     }
 
-    void screenshotIfNeeded(SurfaceControl.Transaction t) {
-        if (mSplitDecorManager != null) {
-            mSplitDecorManager.screenshotIfNeeded(t);
-        }
-    }
-
     void fadeOutDecor(Runnable finishedCallback) {
         if (mSplitDecorManager != null) {
             mSplitDecorManager.fadeOutDecor(finishedCallback, false /* addDelay */);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitScreenController.java
index c5e158c..ea755306 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitScreenController.java
@@ -53,6 +53,7 @@
     private final SyncTransactionQueue mSyncQueue;
     private final Context mContext;
     private final ShellExecutor mMainExecutor;
+    private final ShellExecutor mBgExecutor;
     private final DisplayController mDisplayController;
     private final DisplayImeController mDisplayImeController;
     private final DisplayInsetsController mDisplayInsetsController;
@@ -85,18 +86,20 @@
             SplitState splitState,
             ShellExecutor mainExecutor,
             Handler mainHandler,
+            ShellExecutor bgExecutor,
             SystemWindows systemWindows) {
         super(context, shellInit, shellCommandHandler, shellController, shellTaskOrganizer,
                 syncQueue, rootTDAOrganizer, displayController, displayImeController,
                 displayInsetsController, null, transitions, transactionPool,
                 iconProvider, recentTasks, launchAdjacentController, Optional.empty(),
                 Optional.empty(), null /* stageCoordinator */, multiInstanceHelper, splitState,
-                mainExecutor, mainHandler);
-
+                mainExecutor, mainHandler, bgExecutor);
         mTaskOrganizer = shellTaskOrganizer;
         mSyncQueue = syncQueue;
         mContext = context;
         mMainExecutor = mainExecutor;
+        mMainHandler = mainHandler;
+        mBgExecutor = bgExecutor;
         mDisplayController = displayController;
         mDisplayImeController = displayImeController;
         mDisplayInsetsController = displayInsetsController;
@@ -106,8 +109,6 @@
         mRecentTasksOptional = recentTasks;
         mLaunchAdjacentController = launchAdjacentController;
         mSplitState = splitState;
-
-        mMainHandler = mainHandler;
         mSystemWindows = systemWindows;
     }
 
@@ -120,7 +121,7 @@
         return new TvStageCoordinator(mContext, DEFAULT_DISPLAY, mSyncQueue,
                 mTaskOrganizer, mDisplayController, mDisplayImeController,
                 mDisplayInsetsController, mTransitions, mTransactionPool,
-                mIconProvider, mMainExecutor, mMainHandler,
+                mIconProvider, mMainExecutor, mMainHandler, mBgExecutor,
                 mRecentTasksOptional, mLaunchAdjacentController, mSplitState, mSystemWindows);
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvStageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvStageCoordinator.java
index ef1f88e..9d85bea 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvStageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvStageCoordinator.java
@@ -51,15 +51,15 @@
             DisplayInsetsController displayInsetsController, Transitions transitions,
             TransactionPool transactionPool,
             IconProvider iconProvider, ShellExecutor mainExecutor,
-            Handler mainHandler,
+            Handler mainHandler, ShellExecutor bgExecutor,
             Optional<RecentTasksController> recentTasks,
             LaunchAdjacentController launchAdjacentController,
             SplitState splitState,
             SystemWindows systemWindows) {
         super(context, displayId, syncQueue, taskOrganizer, displayController, displayImeController,
                 displayInsetsController, transitions, transactionPool, iconProvider,
-                mainExecutor, mainHandler, recentTasks, launchAdjacentController, Optional.empty(),
-                splitState);
+                mainExecutor, mainHandler, bgExecutor, recentTasks, launchAdjacentController,
+                Optional.empty(), splitState, Optional.empty());
 
         mTvSplitMenuController = new TvSplitMenuController(context, this,
                 systemWindows, mainHandler);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedTransition.java
index 03ded73..b0547a2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedTransition.java
@@ -204,6 +204,7 @@
         ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
                 "tryAnimateOpenIntentWithRemoteAndPipOrDesktop");
         TransitionInfo.Change pipChange = null;
+        TransitionInfo.Change pipActivityChange = null;
         for (int i = info.getChanges().size() - 1; i >= 0; --i) {
             TransitionInfo.Change change = info.getChanges().get(i);
             if (mPipHandler.isEnteringPip(change, info.getType())) {
@@ -213,6 +214,12 @@
                 }
                 pipChange = change;
                 info.getChanges().remove(i);
+            } else if (change.getTaskInfo() == null && change.getParent() != null
+                    && pipChange != null && change.getParent().equals(pipChange.getContainer())) {
+                // Cache the PiP activity if it's a target and cached pip task change is its parent;
+                // note that we are bottom-to-top, so if such activity has a task
+                // that is also a target, then it must have been cached already as pipChange.
+                pipActivityChange = change;
             }
         }
         TransitionInfo.Change desktopChange = null;
@@ -257,8 +264,16 @@
             // make a new startTransaction because pip's startEnterAnimation "consumes" it so
             // we need a separate one to send over to launcher.
             SurfaceControl.Transaction otherStartT = new SurfaceControl.Transaction();
-
-            mPipHandler.startEnterAnimation(pipChange, otherStartT, finishTransaction, finishCB);
+            if (pipActivityChange == null) {
+                mPipHandler.startEnterAnimation(pipChange, otherStartT, finishTransaction,
+                        finishCB);
+            } else {
+                info.getChanges().remove(pipActivityChange);
+                TransitionInfo pipInfo = subCopy(info, TRANSIT_PIP, false /* withChanges */);
+                pipInfo.getChanges().addAll(List.of(pipChange, pipActivityChange));
+                mPipHandler.startAnimation(mTransition, pipInfo, startTransaction,
+                        finishTransaction, finishCB);
+            }
 
             // Dispatch the rest of the transition normally.
             if (mLeftoversHandler != null
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java
index c385f9a..1c7e62b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java
@@ -76,8 +76,8 @@
                 if (Flags.migratePredictiveBackTransition()) {
                     final boolean gestureToHomeTransition = isBackGesture
                             && TransitionUtil.isClosingType(info.getType());
-                    if (gestureToHomeTransition
-                            || (!isBackGesture && TransitionUtil.isOpenOrCloseMode(mode))) {
+                    if (gestureToHomeTransition || TransitionUtil.isClosingMode(mode)
+                            || (!isBackGesture && TransitionUtil.isOpeningMode(mode))) {
                         notifyHomeVisibilityChanged(gestureToHomeTransition
                                 || TransitionUtil.isOpeningType(mode));
                     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
index 0b91966..792f5ca 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
@@ -468,7 +468,7 @@
                 case MotionEvent.ACTION_DOWN: {
                     mDragPointerId = e.getPointerId(0);
                     mDragPositioningCallback.onDragPositioningStart(
-                            0 /* ctrlType */, e.getRawX(0), e.getRawY(0));
+                            0 /* ctrlType */, e.getDisplayId(), e.getRawX(0), e.getRawY(0));
                     mIsDragging = false;
                     return false;
                 }
@@ -481,6 +481,7 @@
                     if (decoration.isHandlingDragResize()) break;
                     final int dragPointerIdx = e.findPointerIndex(mDragPointerId);
                     mDragPositioningCallback.onDragPositioningMove(
+                            e.getDisplayId(),
                             e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx));
                     mIsDragging = true;
                     return true;
@@ -492,6 +493,7 @@
                     }
                     final int dragPointerIdx = e.findPointerIndex(mDragPointerId);
                     final Rect newTaskBounds = mDragPositioningCallback.onDragPositioningEnd(
+                            e.getDisplayId(),
                             e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx));
                     DragPositioningCallbackUtility.snapTaskBoundsIfNecessary(newTaskBounds,
                             mWindowDecorByTaskId.get(mTaskId).calculateValidDragArea());
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 aea4bda..7928e5e 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
@@ -140,6 +140,7 @@
 import com.android.wm.shell.transition.FocusTransitionObserver;
 import com.android.wm.shell.transition.Transitions;
 import com.android.wm.shell.windowdecor.DesktopModeWindowDecoration.ExclusionRegionListener;
+import com.android.wm.shell.windowdecor.common.WindowDecorTaskResourceLoader;
 import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHost;
 import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHostSupplier;
 import com.android.wm.shell.windowdecor.extension.InsetsStateKt;
@@ -150,7 +151,9 @@
 import kotlin.Unit;
 import kotlin.jvm.functions.Function1;
 
+import kotlinx.coroutines.CoroutineScope;
 import kotlinx.coroutines.ExperimentalCoroutinesApi;
+import kotlinx.coroutines.MainCoroutineDispatcher;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -177,6 +180,8 @@
     private final ShellController mShellController;
     private final Context mContext;
     private final @ShellMainThread Handler mMainHandler;
+    private final @ShellMainThread MainCoroutineDispatcher mMainDispatcher;
+    private final @ShellBackgroundThread CoroutineScope mBgScope;
     private final @ShellBackgroundThread ShellExecutor mBgExecutor;
     private final Choreographer mMainChoreographer;
     private final DisplayController mDisplayController;
@@ -241,12 +246,15 @@
     private final FocusTransitionObserver mFocusTransitionObserver;
     private final DesktopModeEventLogger mDesktopModeEventLogger;
     private final DesktopModeUiEventLogger mDesktopModeUiEventLogger;
+    private final WindowDecorTaskResourceLoader mTaskResourceLoader;
 
     public DesktopModeWindowDecorViewModel(
             Context context,
             ShellExecutor shellExecutor,
             @ShellMainThread Handler mainHandler,
             Choreographer mainChoreographer,
+            @ShellMainThread MainCoroutineDispatcher mainDispatcher,
+            @ShellBackgroundThread CoroutineScope bgScope,
             @ShellBackgroundThread ShellExecutor bgExecutor,
             ShellInit shellInit,
             ShellCommandHandler shellCommandHandler,
@@ -273,12 +281,15 @@
             Optional<DesktopActivityOrientationChangeHandler> activityOrientationChangeHandler,
             FocusTransitionObserver focusTransitionObserver,
             DesktopModeEventLogger desktopModeEventLogger,
-            DesktopModeUiEventLogger desktopModeUiEventLogger) {
+            DesktopModeUiEventLogger desktopModeUiEventLogger,
+            WindowDecorTaskResourceLoader taskResourceLoader) {
         this(
                 context,
                 shellExecutor,
                 mainHandler,
                 mainChoreographer,
+                mainDispatcher,
+                bgScope,
                 bgExecutor,
                 shellInit,
                 shellCommandHandler,
@@ -311,7 +322,8 @@
                 new TaskPositionerFactory(),
                 focusTransitionObserver,
                 desktopModeEventLogger,
-                desktopModeUiEventLogger);
+                desktopModeUiEventLogger,
+                taskResourceLoader);
     }
 
     @VisibleForTesting
@@ -320,6 +332,8 @@
             ShellExecutor shellExecutor,
             @ShellMainThread Handler mainHandler,
             Choreographer mainChoreographer,
+            @ShellMainThread MainCoroutineDispatcher mainDispatcher,
+            @ShellBackgroundThread CoroutineScope bgScope,
             @ShellBackgroundThread ShellExecutor bgExecutor,
             ShellInit shellInit,
             ShellCommandHandler shellCommandHandler,
@@ -352,11 +366,14 @@
             TaskPositionerFactory taskPositionerFactory,
             FocusTransitionObserver focusTransitionObserver,
             DesktopModeEventLogger desktopModeEventLogger,
-            DesktopModeUiEventLogger desktopModeUiEventLogger) {
+            DesktopModeUiEventLogger desktopModeUiEventLogger,
+            WindowDecorTaskResourceLoader taskResourceLoader) {
         mContext = context;
         mMainExecutor = shellExecutor;
         mMainHandler = mainHandler;
         mMainChoreographer = mainChoreographer;
+        mMainDispatcher = mainDispatcher;
+        mBgScope = bgScope;
         mBgExecutor = bgExecutor;
         mActivityTaskManager = mContext.getSystemService(ActivityTaskManager.class);
         mTaskOrganizer = taskOrganizer;
@@ -418,6 +435,7 @@
         mFocusTransitionObserver = focusTransitionObserver;
         mDesktopModeEventLogger = desktopModeEventLogger;
         mDesktopModeUiEventLogger = desktopModeUiEventLogger;
+        mTaskResourceLoader = taskResourceLoader;
 
         shellInit.addInitCallback(this::onInit, this);
     }
@@ -1122,7 +1140,7 @@
                     if (dragAllowed) {
                         mDragPointerId = e.getPointerId(0);
                         final Rect initialBounds = mDragPositioningCallback.onDragPositioningStart(
-                                0 /* ctrlType */, e.getRawX(0),
+                                0 /* ctrlType */, e.getDisplayId(), e.getRawX(0),
                                 e.getRawY(0));
                         updateDragStatus(e.getActionMasked());
                         mOnDragStartInitialBounds.set(initialBounds);
@@ -1143,6 +1161,7 @@
                     }
                     final int dragPointerIdx = e.findPointerIndex(mDragPointerId);
                     final Rect newTaskBounds = mDragPositioningCallback.onDragPositioningMove(
+                            e.getDisplayId(),
                             e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx));
                     mDesktopTasksController.onDragPositioningMove(taskInfo,
                             decoration.mTaskSurface,
@@ -1173,6 +1192,7 @@
                             (int) (e.getRawX(dragPointerIdx) - e.getX(dragPointerIdx)),
                             (int) (e.getRawY(dragPointerIdx) - e.getY(dragPointerIdx)));
                     final Rect newTaskBounds = mDragPositioningCallback.onDragPositioningEnd(
+                            e.getDisplayId(),
                             e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx));
                     // Tasks bounds haven't actually been updated (only its leash), so pass to
                     // DesktopTasksController to allow secondary transformations (i.e. snap resizing
@@ -1640,12 +1660,16 @@
                                 : mContext,
                         mContext.createContextAsUser(UserHandle.of(taskInfo.userId), 0 /* flags */),
                         mDisplayController,
+                        mTaskResourceLoader,
                         mSplitScreenController,
                         mDesktopUserRepositories,
                         mTaskOrganizer,
                         taskInfo,
                         taskSurface,
                         mMainHandler,
+                        mMainExecutor,
+                        mMainDispatcher,
+                        mBgScope,
                         mBgExecutor,
                         mMainChoreographer,
                         mSyncQueue,
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 01319fb..0d1960a 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
@@ -28,7 +28,6 @@
 import static android.window.DesktopModeFlags.ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION_ALWAYS;
 
 
-import static com.android.launcher3.icons.BaseIconFactory.MODE_DEFAULT;
 import static com.android.wm.shell.shared.desktopmode.DesktopModeStatus.canEnterDesktopMode;
 import static com.android.wm.shell.shared.desktopmode.DesktopModeStatus.canEnterDesktopModeOrShowAppHandle;
 import static com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource.APP_HANDLE_MENU_BUTTON;
@@ -49,9 +48,6 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
@@ -60,13 +56,11 @@
 import android.graphics.PointF;
 import android.graphics.Rect;
 import android.graphics.Region;
-import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.os.Handler;
 import android.os.Trace;
 import android.os.UserHandle;
 import android.util.Size;
-import android.util.Slog;
 import android.view.Choreographer;
 import android.view.InsetsState;
 import android.view.MotionEvent;
@@ -81,8 +75,6 @@
 import android.window.WindowContainerTransaction;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.launcher3.icons.BaseIconFactory;
-import com.android.launcher3.icons.IconProvider;
 import com.android.window.flags.Flags;
 import com.android.wm.shell.R;
 import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
@@ -102,10 +94,12 @@
 import com.android.wm.shell.desktopmode.DesktopUserRepositories;
 import com.android.wm.shell.desktopmode.WindowDecorCaptionHandleRepository;
 import com.android.wm.shell.shared.annotations.ShellBackgroundThread;
+import com.android.wm.shell.shared.annotations.ShellMainThread;
 import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
 import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource;
 import com.android.wm.shell.shared.multiinstance.ManageWindowsViewContainer;
 import com.android.wm.shell.splitscreen.SplitScreenController;
+import com.android.wm.shell.windowdecor.common.WindowDecorTaskResourceLoader;
 import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHost;
 import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHostSupplier;
 import com.android.wm.shell.windowdecor.extension.TaskInfoKt;
@@ -118,7 +112,11 @@
 import kotlin.jvm.functions.Function0;
 import kotlin.jvm.functions.Function1;
 
+import kotlinx.coroutines.CoroutineScope;
+import kotlinx.coroutines.MainCoroutineDispatcher;
+
 import java.util.List;
+import java.util.function.BiConsumer;
 import java.util.function.Consumer;
 import java.util.function.Supplier;
 
@@ -134,12 +132,16 @@
     @VisibleForTesting
     static final long CLOSE_MAXIMIZE_MENU_DELAY_MS = 150L;
 
-    private final Handler mHandler;
+    private final @ShellMainThread Handler mHandler;
+    private final @ShellMainThread ShellExecutor mMainExecutor;
+    private final @ShellMainThread MainCoroutineDispatcher mMainDispatcher;
+    private final @ShellBackgroundThread CoroutineScope mBgScope;
     private final @ShellBackgroundThread ShellExecutor mBgExecutor;
     private final Choreographer mChoreographer;
     private final SyncTransactionQueue mSyncQueue;
     private final SplitScreenController mSplitScreenController;
     private final WindowManagerWrapper mWindowManagerWrapper;
+    private final @NonNull WindowDecorTaskResourceLoader mTaskResourceLoader;
 
     private WindowDecorationViewHolder mWindowDecorViewHolder;
     private View.OnClickListener mOnCaptionButtonClickListener;
@@ -175,10 +177,7 @@
     private OpenByDefaultDialog mOpenByDefaultDialog;
 
     private ResizeVeil mResizeVeil;
-    private Bitmap mAppIconBitmap;
-    private Bitmap mResizeVeilBitmap;
 
-    private CharSequence mAppName;
     private CapturedLink mCapturedLink;
     private Uri mGenericLink;
     private Uri mWebUri;
@@ -205,16 +204,23 @@
     private final WindowDecorCaptionHandleRepository mWindowDecorCaptionHandleRepository;
     private final DesktopUserRepositories mDesktopUserRepositories;
 
+    private Runnable mLoadAppInfoRunnable;
+    private Runnable mSetAppInfoRunnable;
+
     public DesktopModeWindowDecoration(
             Context context,
             @NonNull Context userContext,
             DisplayController displayController,
+            @NonNull WindowDecorTaskResourceLoader taskResourceLoader,
             SplitScreenController splitScreenController,
             DesktopUserRepositories desktopUserRepositories,
             ShellTaskOrganizer taskOrganizer,
             ActivityManager.RunningTaskInfo taskInfo,
             SurfaceControl taskSurface,
-            Handler handler,
+            @ShellMainThread Handler handler,
+            @ShellMainThread ShellExecutor mainExecutor,
+            @ShellMainThread MainCoroutineDispatcher mainDispatcher,
+            @ShellBackgroundThread CoroutineScope bgScope,
             @ShellBackgroundThread ShellExecutor bgExecutor,
             Choreographer choreographer,
             SyncTransactionQueue syncQueue,
@@ -226,12 +232,13 @@
             MultiInstanceHelper multiInstanceHelper,
             WindowDecorCaptionHandleRepository windowDecorCaptionHandleRepository,
             DesktopModeEventLogger desktopModeEventLogger) {
-        this (context, userContext, displayController, splitScreenController,
+        this (context, userContext, displayController, taskResourceLoader, splitScreenController,
                 desktopUserRepositories, taskOrganizer, taskInfo, taskSurface, handler,
-                bgExecutor, choreographer, syncQueue, appHeaderViewHolderFactory,
-                rootTaskDisplayAreaOrganizer, genericLinksParser, assistContentRequester,
-                SurfaceControl.Builder::new, SurfaceControl.Transaction::new,
-                WindowContainerTransaction::new, SurfaceControl::new, new WindowManagerWrapper(
+                mainExecutor, mainDispatcher, bgScope, bgExecutor, choreographer, syncQueue,
+                appHeaderViewHolderFactory, rootTaskDisplayAreaOrganizer, genericLinksParser,
+                assistContentRequester, SurfaceControl.Builder::new,
+                SurfaceControl.Transaction::new, WindowContainerTransaction::new,
+                SurfaceControl::new, new WindowManagerWrapper(
                         context.getSystemService(WindowManager.class)),
                 new SurfaceControlViewHostFactory() {},
                 windowDecorViewHostSupplier,
@@ -244,12 +251,16 @@
             Context context,
             @NonNull Context userContext,
             DisplayController displayController,
+            @NonNull WindowDecorTaskResourceLoader taskResourceLoader,
             SplitScreenController splitScreenController,
             DesktopUserRepositories desktopUserRepositories,
             ShellTaskOrganizer taskOrganizer,
             ActivityManager.RunningTaskInfo taskInfo,
             SurfaceControl taskSurface,
-            Handler handler,
+            @ShellMainThread Handler handler,
+            @ShellMainThread ShellExecutor mainExecutor,
+            @ShellMainThread MainCoroutineDispatcher mainDispatcher,
+            @ShellBackgroundThread CoroutineScope bgScope,
             @ShellBackgroundThread ShellExecutor bgExecutor,
             Choreographer choreographer,
             SyncTransactionQueue syncQueue,
@@ -275,6 +286,9 @@
                 surfaceControlViewHostFactory, windowDecorViewHostSupplier, desktopModeEventLogger);
         mSplitScreenController = splitScreenController;
         mHandler = handler;
+        mMainExecutor = mainExecutor;
+        mMainDispatcher = mainDispatcher;
+        mBgScope = bgScope;
         mBgExecutor = bgExecutor;
         mChoreographer = choreographer;
         mSyncQueue = syncQueue;
@@ -288,6 +302,8 @@
         mWindowManagerWrapper = windowManagerWrapper;
         mWindowDecorCaptionHandleRepository = windowDecorCaptionHandleRepository;
         mDesktopUserRepositories = desktopUserRepositories;
+        mTaskResourceLoader = taskResourceLoader;
+        mTaskResourceLoader.onWindowDecorCreated(taskInfo);
     }
 
     /**
@@ -504,6 +520,14 @@
         if (oldRootView != mResult.mRootView) {
             disposeStatusBarInputLayer();
             mWindowDecorViewHolder = createViewHolder();
+            // Load these only when first creating the view.
+            loadTaskNameAndIconInBackground((name, icon) -> {
+                final AppHeaderViewHolder appHeader = asAppHeader(mWindowDecorViewHolder);
+                if (appHeader != null) {
+                    appHeader.setAppName(name);
+                    appHeader.setAppIcon(icon);
+                }
+            });
         }
 
         final Point position = new Point();
@@ -542,6 +566,33 @@
         Trace.endSection(); // DesktopModeWindowDecoration#relayout
     }
 
+    /**
+     * Loads the task's name and icon in a background thread and posts the results back in the
+     * main thread.
+     */
+    private void loadTaskNameAndIconInBackground(BiConsumer<CharSequence, Bitmap> onResult) {
+        if (mWindowDecorViewHolder == null) return;
+        if (asAppHeader(mWindowDecorViewHolder) == null) {
+            // Only needed when drawing a header.
+            return;
+        }
+        if (mLoadAppInfoRunnable != null) {
+            mBgExecutor.removeCallbacks(mLoadAppInfoRunnable);
+        }
+        if (mSetAppInfoRunnable != null) {
+            mMainExecutor.removeCallbacks(mSetAppInfoRunnable);
+        }
+        mLoadAppInfoRunnable = () -> {
+            final CharSequence name = mTaskResourceLoader.getName(mTaskInfo);
+            final Bitmap icon = mTaskResourceLoader.getHeaderIcon(mTaskInfo);
+            mSetAppInfoRunnable = () -> {
+                onResult.accept(name, icon);
+            };
+            mMainExecutor.execute(mSetAppInfoRunnable);
+        };
+        mBgExecutor.execute(mLoadAppInfoRunnable);
+    }
+
     private boolean isCaptionVisible() {
         return mTaskInfo.isVisible && mIsCaptionVisible;
     }
@@ -566,14 +617,16 @@
         }
 
         if (browserLink == null) return null;
-        return AppToWebUtils.getBrowserIntent(browserLink, mContext.getPackageManager());
+        return AppToWebUtils.getBrowserIntent(browserLink, mContext.getPackageManager(),
+                mUserContext.getUserId());
 
     }
 
     @Nullable
     private Intent getAppLink() {
         return mWebUri == null ? null
-                : AppToWebUtils.getAppIntent(mWebUri, mContext.getPackageManager());
+                : AppToWebUtils.getAppIntent(mWebUri, mContext.getPackageManager(),
+                        mUserContext.getUserId());
     }
 
     private boolean isBrowserApp() {
@@ -728,12 +781,17 @@
         final Point position = new Point(mResult.mCaptionX, 0);
         if (mSplitScreenController.getSplitPosition(mTaskInfo.taskId)
                 == SPLIT_POSITION_BOTTOM_OR_RIGHT
-                && mDisplayController.getDisplayLayout(mTaskInfo.displayId).isLandscape()
         ) {
-            // If this is the right split task, add left stage's width.
-            final Rect leftStageBounds = new Rect();
-            mSplitScreenController.getStageBounds(leftStageBounds, new Rect());
-            position.x += leftStageBounds.width();
+            if (mSplitScreenController.isLeftRightSplit()) {
+                // If this is the right split task, add left stage's width.
+                final Rect leftStageBounds = new Rect();
+                mSplitScreenController.getStageBounds(leftStageBounds, new Rect());
+                position.x += leftStageBounds.width();
+            } else {
+                final Rect bottomStageBounds = new Rect();
+                mSplitScreenController.getRefStageBounds(new Rect(), bottomStageBounds);
+                position.y += bottomStageBounds.top;
+            }
         }
         return position;
     }
@@ -761,15 +819,12 @@
             );
         } else if (mRelayoutParams.mLayoutResId
                 == R.layout.desktop_mode_app_header) {
-            loadAppInfoIfNeeded();
             return mAppHeaderViewHolderFactory.create(
                     mResult.mRootView,
                     mOnCaptionTouchListener,
                     mOnCaptionButtonClickListener,
                     mOnCaptionLongClickListener,
                     mOnCaptionGenericMotionListener,
-                    mAppName,
-                    mAppIconBitmap,
                     mOnMaximizeHoverListener);
         }
         throw new IllegalArgumentException("Unexpected layout resource id");
@@ -1033,7 +1088,10 @@
                 mTaskInfo,
                 mTaskSurface,
                 mDisplayController,
+                mTaskResourceLoader,
                 mSurfaceControlTransactionSupplier,
+                mMainDispatcher,
+                mBgScope,
                 new OpenByDefaultDialog.DialogLifecycleListener() {
                     @Override
                     public void onDialogCreated() {
@@ -1044,9 +1102,7 @@
                     public void onDialogDismissed() {
                         mOpenByDefaultDialog = null;
                     }
-                },
-                mAppIconBitmap,
-                mAppName
+                }
         );
     }
 
@@ -1058,50 +1114,6 @@
         return mDragResizeListener != null && mDragResizeListener.isHandlingDragResize();
     }
 
-    private void loadAppInfoIfNeeded() {
-        // TODO(b/337370277): move this to another thread.
-        try {
-            Trace.beginSection("DesktopModeWindowDecoration#loadAppInfoIfNeeded");
-            if (mAppIconBitmap != null && mAppName != null) {
-                return;
-            }
-            if (mTaskInfo.baseIntent == null) {
-                Slog.e(TAG, "Base intent not found in task");
-                return;
-            }
-            final PackageManager pm = mUserContext.getPackageManager();
-            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,
-                    UserHandle.of(mTaskInfo.userId));
-            final BaseIconFactory headerIconFactory = createIconFactory(mContext,
-                    R.dimen.desktop_mode_caption_icon_radius);
-            mAppIconBitmap = headerIconFactory.createIconBitmap(badgedAppIconDrawable,
-                    1f /* scale */);
-
-            final BaseIconFactory resizeVeilIconFactory = createIconFactory(mContext,
-                    R.dimen.desktop_mode_resize_veil_icon_size);
-            mResizeVeilBitmap = resizeVeilIconFactory
-                    .createScaledBitmap(appIconDrawable, MODE_DEFAULT);
-
-            final ApplicationInfo applicationInfo = activityInfo.applicationInfo;
-            mAppName = pm.getApplicationLabel(applicationInfo);
-        } catch (PackageManager.NameNotFoundException e) {
-            Slog.e(TAG, "Base activity's component name cannot be found on the system", e);
-        } finally {
-            Trace.endSection();
-        }
-    }
-
-    private BaseIconFactory createIconFactory(Context context, int dimensions) {
-        final Resources resources = context.getResources();
-        final int densityDpi = resources.getDisplayMetrics().densityDpi;
-        final int iconSize = resources.getDimensionPixelSize(dimensions);
-        return new BaseIconFactory(context, densityDpi, iconSize);
-    }
-
     private void closeDragResizeListener() {
         if (mDragResizeListener == null) {
             return;
@@ -1116,9 +1128,9 @@
      */
     private void createResizeVeilIfNeeded() {
         if (mResizeVeil != null) return;
-        loadAppInfoIfNeeded();
-        mResizeVeil = new ResizeVeil(mContext, mDisplayController, mResizeVeilBitmap,
-                mTaskSurface, mSurfaceControlTransactionSupplier, mTaskInfo);
+        mResizeVeil = new ResizeVeil(mContext, mDisplayController, mTaskResourceLoader,
+                mMainDispatcher, mBgScope, mTaskSurface,
+                mSurfaceControlTransactionSupplier, mTaskInfo);
     }
 
     /**
@@ -1318,7 +1330,6 @@
     @VisibleForTesting
     void onAssistContentReceived(@Nullable AssistContent assistContent) {
         mWebUri = assistContent == null ? null : AppToWebUtils.getSessionWebUri(assistContent);
-        loadAppInfoIfNeeded();
         updateGenericLink();
         final boolean supportsMultiInstance = mMultiInstanceHelper
                 .supportsMultiInstanceSplit(mTaskInfo.baseActivity)
@@ -1331,11 +1342,12 @@
                 .isTaskInFullImmersiveState(mTaskInfo.taskId);
         final boolean isBrowserApp = isBrowserApp();
         mHandleMenu = mHandleMenuFactory.create(
+                mMainDispatcher,
+                mBgScope,
                 this,
                 mWindowManagerWrapper,
+                mTaskResourceLoader,
                 mRelayoutParams.mLayoutResId,
-                mAppIconBitmap,
-                mAppName,
                 mSplitScreenController,
                 canEnterDesktopModeOrShowAppHandle(mContext),
                 supportsMultiInstance,
@@ -1624,12 +1636,20 @@
 
     @Override
     public void close() {
+        if (mLoadAppInfoRunnable != null) {
+            mBgExecutor.removeCallbacks(mLoadAppInfoRunnable);
+        }
+        if (mSetAppInfoRunnable != null) {
+            mMainExecutor.removeCallbacks(mSetAppInfoRunnable);
+        }
+        mTaskResourceLoader.onWindowDecorClosed(mTaskInfo);
         closeDragResizeListener();
         closeHandleMenu();
         closeManageWindowsMenu();
         mExclusionRegionListener.onExclusionRegionDismissed(mTaskInfo.taskId);
         disposeResizeVeil();
         disposeStatusBarInputLayer();
+        mWindowDecorViewHolder = null;
         if (canEnterDesktopMode(mContext) && isEducationEnabled()) {
             notifyNoCaptionHandle();
         }
@@ -1753,12 +1773,16 @@
                 Context context,
                 @NonNull Context userContext,
                 DisplayController displayController,
+                @NonNull WindowDecorTaskResourceLoader appResourceProvider,
                 SplitScreenController splitScreenController,
                 DesktopUserRepositories desktopUserRepositories,
                 ShellTaskOrganizer taskOrganizer,
                 ActivityManager.RunningTaskInfo taskInfo,
                 SurfaceControl taskSurface,
-                Handler handler,
+                @ShellMainThread Handler handler,
+                @ShellMainThread ShellExecutor mainExecutor,
+                @ShellMainThread MainCoroutineDispatcher mainDispatcher,
+                @ShellBackgroundThread CoroutineScope bgScope,
                 @ShellBackgroundThread ShellExecutor bgExecutor,
                 Choreographer choreographer,
                 SyncTransactionQueue syncQueue,
@@ -1775,12 +1799,16 @@
                     context,
                     userContext,
                     displayController,
+                    appResourceProvider,
                     splitScreenController,
                     desktopUserRepositories,
                     taskOrganizer,
                     taskInfo,
                     taskSurface,
                     handler,
+                    mainExecutor,
+                    mainDispatcher,
+                    bgScope,
                     bgExecutor,
                     choreographer,
                     syncQueue,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallback.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallback.java
index 421ffd9..3eebdb04 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallback.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallback.java
@@ -41,25 +41,30 @@
      *
      * @param ctrlType {@link CtrlType} indicating the direction of resizing, use
      *                 {@code 0} to indicate it's a move
+     * @param displayId the ID of the display where the drag starts
      * @param x x coordinate in window decoration coordinate system where the drag starts
      * @param y y coordinate in window decoration coordinate system where the drag starts
      * @return the starting task bounds
      */
-    Rect onDragPositioningStart(@CtrlType int ctrlType, float x, float y);
+    Rect onDragPositioningStart(@CtrlType int ctrlType, int displayId, float x, float y);
 
     /**
      * Called when the pointer moves during a drag-resize or drag-move.
+     *
+     * @param displayId the ID of the display where the pointer is currently located
      * @param x x coordinate in window decoration coordinate system of the new pointer location
      * @param y y coordinate in window decoration coordinate system of the new pointer location
      * @return the updated task bounds
      */
-    Rect onDragPositioningMove(float x, float y);
+    Rect onDragPositioningMove(int displayId, float x, float y);
 
     /**
      * Called when a drag-resize or drag-move stops.
+     *
+     * @param displayId the ID of the display where the pointer is located when drag stops
      * @param x x coordinate in window decoration coordinate system where the drag resize stops
      * @param y y coordinate in window decoration coordinate system where the drag resize stops
      * @return the final bounds for the dragged task
      */
-    Rect onDragPositioningEnd(float x, float y);
+    Rect onDragPositioningEnd(int displayId, float x, float y);
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
index a6d503d..7d1471f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
@@ -454,7 +454,7 @@
                         ProtoLog.d(WM_SHELL_DESKTOP_MODE,
                                 "%s: Handling action down, update ctrlType to %d", TAG, ctrlType);
                         mDragStartTaskBounds = mCallback.onDragPositioningStart(ctrlType,
-                                rawX, rawY);
+                                e.getDisplayId(), rawX, rawY);
                         mLastMotionEventOnDown = e;
                         mResizeTrigger = (ctrlType == CTRL_TYPE_BOTTOM || ctrlType == CTRL_TYPE_TOP
                                 || ctrlType == CTRL_TYPE_RIGHT || ctrlType == CTRL_TYPE_LEFT)
@@ -489,7 +489,8 @@
                     }
                     final float rawX = e.getRawX(dragPointerIndex);
                     final float rawY = e.getRawY(dragPointerIndex);
-                    final Rect taskBounds = mCallback.onDragPositioningMove(rawX, rawY);
+                    final Rect taskBounds = mCallback.onDragPositioningMove(e.getDisplayId(),
+                            rawX, rawY);
                     updateInputSinkRegionForDrag(taskBounds);
                     result = true;
                     break;
@@ -505,7 +506,7 @@
                                     TAG, e.getActionMasked());
                             break;
                         }
-                        final Rect taskBounds = mCallback.onDragPositioningEnd(
+                        final Rect taskBounds = mCallback.onDragPositioningEnd(e.getDisplayId(),
                                 e.getRawX(dragPointerIndex), e.getRawY(dragPointerIndex));
                         // If taskBounds has changed, setGeometry will be called and update the
                         // sink region. Otherwise, we should revert it here.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeWindowGeometry.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeWindowGeometry.java
index c8aff78..5b027f3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeWindowGeometry.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeWindowGeometry.java
@@ -168,7 +168,10 @@
         return (e.getSource() & SOURCE_TOUCHSCREEN) == SOURCE_TOUCHSCREEN;
     }
 
-    static boolean isEdgeResizePermitted(@NonNull MotionEvent e) {
+    /**
+     * Whether resizing a window from the edge is permitted based on the motion event.
+     */
+    public static boolean isEdgeResizePermitted(@NonNull MotionEvent e) {
         if (ENABLE_WINDOWING_EDGE_DRAG_RESIZE.isTrue()) {
             return e.getToolType(0) == MotionEvent.TOOL_TYPE_STYLUS
                     || e.getToolType(0) == MotionEvent.TOOL_TYPE_MOUSE
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FixedAspectRatioTaskPositionerDecorator.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FixedAspectRatioTaskPositionerDecorator.kt
index 3885761..ab30d61 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FixedAspectRatioTaskPositionerDecorator.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FixedAspectRatioTaskPositionerDecorator.kt
@@ -47,10 +47,11 @@
     private var startingAspectRatio = 0f
     private var isTaskPortrait = false
 
-    override fun onDragPositioningStart(@CtrlType ctrlType: Int, x: Float, y: Float): Rect {
+    override fun onDragPositioningStart(
+        @CtrlType ctrlType: Int, displayId: Int, x: Float, y: Float): Rect {
         originalCtrlType = ctrlType
         if (!requiresFixedAspectRatio()) {
-            return super.onDragPositioningStart(originalCtrlType, x, y)
+            return super.onDragPositioningStart(originalCtrlType, displayId, x, y)
         }
 
         lastRepositionedBounds.set(getBounds(windowDecoration.mTaskInfo))
@@ -72,27 +73,27 @@
                     val verticalMidPoint = lastRepositionedBounds.top + (startingBoundHeight / 2)
                     edgeResizeCtrlType = originalCtrlType +
                             if (y < verticalMidPoint) CTRL_TYPE_TOP else CTRL_TYPE_BOTTOM
-                    super.onDragPositioningStart(edgeResizeCtrlType, x, y)
+                    super.onDragPositioningStart(edgeResizeCtrlType, displayId, x, y)
                 }
                 CTRL_TYPE_TOP, CTRL_TYPE_BOTTOM -> {
                     val horizontalMidPoint = lastRepositionedBounds.left + (startingBoundWidth / 2)
                     edgeResizeCtrlType = originalCtrlType +
                             if (x < horizontalMidPoint) CTRL_TYPE_LEFT else CTRL_TYPE_RIGHT
-                    super.onDragPositioningStart(edgeResizeCtrlType, x, y)
+                    super.onDragPositioningStart(edgeResizeCtrlType, displayId, x, y)
                 }
                 // If resize is corner resize, no alteration to the ctrlType needs to be made.
                 else -> {
                     edgeResizeCtrlType = CTRL_TYPE_UNDEFINED
-                    super.onDragPositioningStart(originalCtrlType, x, y)
+                    super.onDragPositioningStart(originalCtrlType, displayId, x, y)
                 }
             }
         )
         return lastRepositionedBounds
     }
 
-    override fun onDragPositioningMove(x: Float, y: Float): Rect {
+    override fun onDragPositioningMove(displayId: Int, x: Float, y: Float): Rect {
         if (!requiresFixedAspectRatio()) {
-            return super.onDragPositioningMove(x, y)
+            return super.onDragPositioningMove(displayId, x, y)
         }
 
         val diffX = x - lastValidPoint.x
@@ -103,7 +104,7 @@
                     // Drag coordinate falls within valid region (90 - 180 degrees or 270- 360
                     // degrees from the corner the previous valid point). Allow resize with adjusted
                     // coordinates to maintain aspect ratio.
-                    lastRepositionedBounds.set(dragAdjustedMove(x, y))
+                    lastRepositionedBounds.set(dragAdjustedMove(displayId, x, y))
                 }
             }
             CTRL_TYPE_BOTTOM + CTRL_TYPE_LEFT, CTRL_TYPE_TOP + CTRL_TYPE_RIGHT -> {
@@ -111,28 +112,28 @@
                     // Drag coordinate falls within valid region (180 - 270 degrees or 0 - 90
                     // degrees from the corner the previous valid point). Allow resize with adjusted
                     // coordinates to maintain aspect ratio.
-                    lastRepositionedBounds.set(dragAdjustedMove(x, y))
+                    lastRepositionedBounds.set(dragAdjustedMove(displayId, x, y))
                 }
             }
             CTRL_TYPE_LEFT, CTRL_TYPE_RIGHT -> {
                 // If resize is on left or right edge, always adjust the y coordinate.
                 val adjustedY = getScaledChangeForY(x)
                 lastValidPoint.set(x, adjustedY)
-                lastRepositionedBounds.set(super.onDragPositioningMove(x, adjustedY))
+                lastRepositionedBounds.set(super.onDragPositioningMove(displayId, x, adjustedY))
             }
             CTRL_TYPE_TOP, CTRL_TYPE_BOTTOM -> {
                 // If resize is on top or bottom edge, always adjust the x coordinate.
                 val adjustedX = getScaledChangeForX(y)
                 lastValidPoint.set(adjustedX, y)
-                lastRepositionedBounds.set(super.onDragPositioningMove(adjustedX, y))
+                lastRepositionedBounds.set(super.onDragPositioningMove(displayId, adjustedX, y))
             }
         }
         return lastRepositionedBounds
     }
 
-    override fun onDragPositioningEnd(x: Float, y: Float): Rect {
+    override fun onDragPositioningEnd(displayId: Int, x: Float, y: Float): Rect {
         if (!requiresFixedAspectRatio()) {
-            return super.onDragPositioningEnd(x, y)
+            return super.onDragPositioningEnd(displayId, x, y)
         }
 
         val diffX = x - lastValidPoint.x
@@ -144,55 +145,55 @@
                     // Drag coordinate falls within valid region (90 - 180 degrees or 270- 360
                     // degrees from the corner the previous valid point). End resize with adjusted
                     // coordinates to maintain aspect ratio.
-                    return dragAdjustedEnd(x, y)
+                    return dragAdjustedEnd(displayId, x, y)
                 }
                 // If end of resize is not within valid region, end resize from last valid
                 // coordinates.
-                return super.onDragPositioningEnd(lastValidPoint.x, lastValidPoint.y)
+                return super.onDragPositioningEnd(displayId, lastValidPoint.x, lastValidPoint.y)
             }
             CTRL_TYPE_BOTTOM + CTRL_TYPE_LEFT, CTRL_TYPE_TOP + CTRL_TYPE_RIGHT -> {
                 if ((diffX > 0 && diffY < 0) || (diffX < 0 && diffY > 0)) {
                     // Drag coordinate falls within valid region (180 - 260 degrees or 0 - 90
                     // degrees from the corner the previous valid point). End resize with adjusted
                     // coordinates to maintain aspect ratio.
-                    return dragAdjustedEnd(x, y)
+                    return dragAdjustedEnd(displayId, x, y)
                 }
                 // If end of resize is not within valid region, end resize from last valid
                 // coordinates.
-                return super.onDragPositioningEnd(lastValidPoint.x, lastValidPoint.y)
+                return super.onDragPositioningEnd(displayId, lastValidPoint.x, lastValidPoint.y)
             }
             CTRL_TYPE_LEFT, CTRL_TYPE_RIGHT -> {
                 // If resize is on left or right edge, always adjust the y coordinate.
-                return super.onDragPositioningEnd(x, getScaledChangeForY(x))
+                return super.onDragPositioningEnd(displayId, x, getScaledChangeForY(x))
             }
             CTRL_TYPE_TOP, CTRL_TYPE_BOTTOM -> {
                 // If resize is on top or bottom edge, always adjust the x coordinate.
-                return super.onDragPositioningEnd(getScaledChangeForX(y), y)
+                return super.onDragPositioningEnd(displayId, getScaledChangeForX(y), y)
             }
             else -> {
-                return super.onDragPositioningEnd(x, y)
+                return super.onDragPositioningEnd(displayId, x, y)
             }
         }
     }
 
-    private fun dragAdjustedMove(x: Float, y: Float): Rect {
+    private fun dragAdjustedMove(displayId: Int, x: Float, y: Float): Rect {
         val absDiffX = abs(x - lastValidPoint.x)
         val absDiffY = abs(y - lastValidPoint.y)
         if (absDiffY < absDiffX) {
             lastValidPoint.set(getScaledChangeForX(y), y)
-            return super.onDragPositioningMove(getScaledChangeForX(y), y)
+            return super.onDragPositioningMove(displayId, getScaledChangeForX(y), y)
         }
         lastValidPoint.set(x, getScaledChangeForY(x))
-        return super.onDragPositioningMove(x, getScaledChangeForY(x))
+        return super.onDragPositioningMove(displayId, x, getScaledChangeForY(x))
     }
 
-    private fun dragAdjustedEnd(x: Float, y: Float): Rect {
+    private fun dragAdjustedEnd(displayId: Int, x: Float, y: Float): Rect {
         val absDiffX = abs(x - lastValidPoint.x)
         val absDiffY = abs(y - lastValidPoint.y)
         if (absDiffY < absDiffX) {
-            return super.onDragPositioningEnd(getScaledChangeForX(y), y)
+            return super.onDragPositioningEnd(displayId, getScaledChangeForX(y), y)
         }
-        return super.onDragPositioningEnd(x, getScaledChangeForY(x))
+        return super.onDragPositioningEnd(displayId, x, getScaledChangeForY(x))
     }
 
     /**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositioner.java
index 3efae9d..2d6f745 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositioner.java
@@ -91,7 +91,7 @@
     }
 
     @Override
-    public Rect onDragPositioningStart(int ctrlType, float x, float y) {
+    public Rect onDragPositioningStart(int ctrlType, int displayId, float x, float y) {
         mCtrlType = ctrlType;
         mTaskBoundsAtDragStart.set(
                 mWindowDecoration.mTaskInfo.configuration.windowConfiguration.getBounds());
@@ -117,7 +117,7 @@
     }
 
     @Override
-    public Rect onDragPositioningMove(float x, float y) {
+    public Rect onDragPositioningMove(int displayId, float x, float y) {
         final WindowContainerTransaction wct = new WindowContainerTransaction();
         PointF delta = DragPositioningCallbackUtility.calculateDelta(x, y, mRepositionStartPoint);
         if (isResizing() && DragPositioningCallbackUtility.changeBounds(mCtrlType,
@@ -147,7 +147,7 @@
     }
 
     @Override
-    public Rect onDragPositioningEnd(float x, float y) {
+    public Rect onDragPositioningEnd(int displayId, float x, float y) {
         // If task has been resized or task was dragged into area outside of
         // mDisallowedAreaForEndBounds, apply WCT to finish it.
         if (isResizing() && mHasDragResized) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt
index 1179b0c..bb19a2c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt
@@ -47,15 +47,25 @@
 import androidx.core.view.isGone
 import com.android.window.flags.Flags
 import com.android.wm.shell.R
+import com.android.wm.shell.shared.annotations.ShellBackgroundThread
+import com.android.wm.shell.shared.annotations.ShellMainThread
 import com.android.wm.shell.shared.split.SplitScreenConstants
 import com.android.wm.shell.splitscreen.SplitScreenController
 import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalSystemViewContainer
 import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalViewContainer
 import com.android.wm.shell.windowdecor.common.DecorThemeUtil
 import com.android.wm.shell.windowdecor.common.calculateMenuPosition
+import com.android.wm.shell.windowdecor.common.WindowDecorTaskResourceLoader
 import com.android.wm.shell.windowdecor.extension.isFullscreen
 import com.android.wm.shell.windowdecor.extension.isMultiWindow
 import com.android.wm.shell.windowdecor.extension.isPinned
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.MainCoroutineDispatcher
+import kotlinx.coroutines.isActive
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
 
 /**
  * Handle menu opened when the appropriate button is clicked on.
@@ -66,11 +76,12 @@
  * Additional Options: Miscellaneous functions including screenshot and closing task.
  */
 class HandleMenu(
+    @ShellMainThread private val mainDispatcher: CoroutineDispatcher,
+    @ShellBackgroundThread private val bgScope: CoroutineScope,
     private val parentDecor: DesktopModeWindowDecoration,
     private val windowManagerWrapper: WindowManagerWrapper,
+    private val taskResourceLoader: WindowDecorTaskResourceLoader,
     private val layoutResId: Int,
-    private val appIconBitmap: Bitmap?,
-    private val appName: CharSequence?,
     private val splitScreenController: SplitScreenController,
     private val shouldShowWindowingPill: Boolean,
     private val shouldShowNewWindowButton: Boolean,
@@ -103,7 +114,8 @@
 
     @VisibleForTesting
     var handleMenuViewContainer: AdditionalViewContainer? = null
-    private var handleMenuView: HandleMenuView? = null
+    @VisibleForTesting
+    var handleMenuView: HandleMenuView? = null
 
     // Position of the handle menu used for laying out the handle view.
     @VisibleForTesting
@@ -122,6 +134,8 @@
         get() = SHOULD_SHOW_SCREENSHOT_BUTTON || shouldShowNewWindowButton ||
             shouldShowManageWindowsButton || shouldShowChangeAspectRatioButton
 
+    private var loadAppInfoJob: Job? = null
+
     init {
         updateHandleMenuPillPositions(captionX, captionY)
     }
@@ -190,7 +204,7 @@
             shouldShowDesktopModeButton = shouldShowDesktopModeButton,
             isBrowserApp = isBrowserApp
         ).apply {
-            bind(taskInfo, appIconBitmap, appName, shouldShowMoreActionsPill)
+            bind(taskInfo, shouldShowMoreActionsPill)
             this.onToDesktopClickListener = onToDesktopClickListener
             this.onToFullscreenClickListener = onToFullscreenClickListener
             this.onToSplitScreenClickListener = onToSplitScreenClickListener
@@ -204,7 +218,16 @@
             this.onCloseMenuClickListener = onCloseMenuClickListener
             this.onOutsideTouchListener = onOutsideTouchListener
         }
-
+        loadAppInfoJob = bgScope.launch {
+            if (!isActive) return@launch
+            val name = taskResourceLoader.getName(taskInfo)
+            val icon = taskResourceLoader.getHeaderIcon(taskInfo)
+            withContext(mainDispatcher) {
+                if (!isActive) return@withContext
+                handleMenuView.setAppName(name)
+                handleMenuView.setAppIcon(icon)
+            }
+        }
         val x = handleMenuPosition.x.toInt()
         val y = handleMenuPosition.y.toInt()
         handleMenuViewContainer =
@@ -412,6 +435,7 @@
         resources.configuration.layoutDirection == View.LAYOUT_DIRECTION_RTL
 
     fun close() {
+        loadAppInfoJob?.cancel()
         handleMenuView?.animateCloseMenu {
             handleMenuViewContainer?.releaseView()
             handleMenuViewContainer = null
@@ -439,8 +463,10 @@
         private val appInfoPill = rootView.requireViewById<View>(R.id.app_info_pill)
         private val collapseMenuButton = appInfoPill.requireViewById<HandleMenuImageButton>(
             R.id.collapse_menu_button)
-        private val appIconView = appInfoPill.requireViewById<ImageView>(R.id.application_icon)
-        private val appNameView = appInfoPill.requireViewById<TextView>(R.id.application_name)
+        @VisibleForTesting
+        val appIconView = appInfoPill.requireViewById<ImageView>(R.id.application_icon)
+        @VisibleForTesting
+        val appNameView = appInfoPill.requireViewById<TextView>(R.id.application_name)
 
         // Windowing Pill.
         private val windowingPill = rootView.requireViewById<View>(R.id.windowing_pill)
@@ -509,14 +535,12 @@
         /** Binds the menu views to the new data. */
         fun bind(
             taskInfo: RunningTaskInfo,
-            appIconBitmap: Bitmap?,
-            appName: CharSequence?,
             shouldShowMoreActionsPill: Boolean
         ) {
             this.taskInfo = taskInfo
             this.style = calculateMenuStyle(taskInfo)
 
-            bindAppInfoPill(style, appIconBitmap, appName)
+            bindAppInfoPill(style)
             if (shouldShowWindowingPill) {
                 bindWindowingPill(style)
             }
@@ -527,6 +551,16 @@
             bindOpenInAppOrBrowserPill(style)
         }
 
+        /** Sets the app's name. */
+        fun setAppName(name: CharSequence) {
+            appNameView.text = name
+        }
+
+        /** Sets the app's icon. */
+        fun setAppIcon(icon: Bitmap) {
+            appIconView.setImageBitmap(icon)
+        }
+
         /** Animates the menu openInAppOrBrowserg. */
         fun animateOpenMenu() {
             if (taskInfo.isFullscreen || taskInfo.isMultiWindow) {
@@ -593,22 +627,14 @@
             )
         }
 
-        private fun bindAppInfoPill(
-            style: MenuStyle,
-            appIconBitmap: Bitmap?,
-            appName: CharSequence?
-        ) {
+        private fun bindAppInfoPill(style: MenuStyle) {
             appInfoPill.background.setTint(style.backgroundColor)
 
             collapseMenuButton.apply {
                 imageTintList = ColorStateList.valueOf(style.textColor)
                 this.taskInfo = this@HandleMenuView.taskInfo
             }
-            appIconView.setImageBitmap(appIconBitmap)
-            appNameView.apply {
-                text = appName
-                setTextColor(style.textColor)
-            }
+            appNameView.setTextColor(style.textColor)
         }
 
         private fun bindWindowingPill(style: MenuStyle) {
@@ -668,7 +694,7 @@
                 setTextColor(style.textColor)
                 compoundDrawableTintList = ColorStateList.valueOf(style.textColor)
             }
-
+            openByDefaultBtn.isGone = isBrowserApp
             openByDefaultBtn.imageTintList = ColorStateList.valueOf(style.textColor)
         }
 
@@ -698,11 +724,12 @@
 /** A factory interface to create a [HandleMenu]. */
 interface HandleMenuFactory {
     fun create(
+        @ShellMainThread mainDispatcher: MainCoroutineDispatcher,
+        @ShellBackgroundThread bgScope: CoroutineScope,
         parentDecor: DesktopModeWindowDecoration,
         windowManagerWrapper: WindowManagerWrapper,
+        taskResourceLoader: WindowDecorTaskResourceLoader,
         layoutResId: Int,
-        appIconBitmap: Bitmap?,
-        appName: CharSequence?,
         splitScreenController: SplitScreenController,
         shouldShowWindowingPill: Boolean,
         shouldShowNewWindowButton: Boolean,
@@ -721,11 +748,12 @@
 /** A [HandleMenuFactory] implementation that creates a [HandleMenu].  */
 object DefaultHandleMenuFactory : HandleMenuFactory {
     override fun create(
+        @ShellMainThread mainDispatcher: MainCoroutineDispatcher,
+        @ShellBackgroundThread bgScope: CoroutineScope,
         parentDecor: DesktopModeWindowDecoration,
         windowManagerWrapper: WindowManagerWrapper,
+        taskResourceLoader: WindowDecorTaskResourceLoader,
         layoutResId: Int,
-        appIconBitmap: Bitmap?,
-        appName: CharSequence?,
         splitScreenController: SplitScreenController,
         shouldShowWindowingPill: Boolean,
         shouldShowNewWindowButton: Boolean,
@@ -740,11 +768,12 @@
         captionY: Int,
     ): HandleMenu {
         return HandleMenu(
+            mainDispatcher,
+            bgScope,
             parentDecor,
             windowManagerWrapper,
+            taskResourceLoader,
             layoutResId,
-            appIconBitmap,
-            appName,
             splitScreenController,
             shouldShowWindowingPill,
             shouldShowNewWindowButton,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeButtonView.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeButtonView.kt
index 376cd2a..e23ebe6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeButtonView.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeButtonView.kt
@@ -26,6 +26,7 @@
 import android.util.AttributeSet
 import android.view.LayoutInflater
 import android.view.View
+import android.view.ViewStub
 import android.widget.FrameLayout
 import android.widget.ImageButton
 import android.widget.ProgressBar
@@ -46,13 +47,17 @@
     private val hoverProgressAnimatorSet = AnimatorSet()
     var hoverDisabled = false
 
-    private val progressBar: ProgressBar
+    private lateinit var stubProgressBarContainer: ViewStub
     private val maximizeWindow: ImageButton
+    private val progressBar: ProgressBar by lazy {
+        (stubProgressBarContainer.inflate() as FrameLayout)
+            .requireViewById(R.id.progress_bar)
+    }
 
     init {
         LayoutInflater.from(context).inflate(R.layout.maximize_menu_button, this, true)
 
-        progressBar = requireViewById(R.id.progress_bar)
+        stubProgressBarContainer = requireViewById(R.id.stub_progress_bar_container)
         maximizeWindow = requireViewById(R.id.maximize_window)
     }
 
@@ -115,21 +120,34 @@
             requireNotNull(rippleDrawable) { "Ripple drawable must be non-null" }
             maximizeWindow.imageTintList = iconForegroundColor
             maximizeWindow.background = rippleDrawable
-            progressBar.progressTintList = ColorStateList.valueOf(baseForegroundColor)
-                .withAlpha(OPACITY_15)
-            progressBar.progressBackgroundTintList = ColorStateList.valueOf(Color.TRANSPARENT)
-        } else {
-            if (darkMode) {
-                progressBar.progressTintList = ColorStateList.valueOf(
-                    resources.getColor(R.color.desktop_mode_maximize_menu_progress_dark))
-                maximizeWindow.background?.setTintList(ContextCompat.getColorStateList(context,
-                    R.color.desktop_mode_caption_button_color_selector_dark))
-            } else {
-                progressBar.progressTintList = ColorStateList.valueOf(
-                    resources.getColor(R.color.desktop_mode_maximize_menu_progress_light))
-                maximizeWindow.background?.setTintList(ContextCompat.getColorStateList(context,
-                    R.color.desktop_mode_caption_button_color_selector_light))
+            stubProgressBarContainer.setOnInflateListener { _, inflated ->
+                val progressBar = (inflated as FrameLayout)
+                    .requireViewById(R.id.progress_bar) as ProgressBar
+                progressBar.progressTintList = ColorStateList.valueOf(baseForegroundColor)
+                    .withAlpha(OPACITY_15)
+                progressBar.progressBackgroundTintList = ColorStateList.valueOf(Color.TRANSPARENT)
             }
+        } else {
+            val progressTint = if (darkMode) {
+                ColorStateList.valueOf(
+                    resources.getColor(R.color.desktop_mode_maximize_menu_progress_dark))
+            } else {
+                ColorStateList.valueOf(
+                    resources.getColor(R.color.desktop_mode_maximize_menu_progress_light))
+            }
+            val backgroundTint = if (darkMode) {
+                ContextCompat.getColorStateList(context,
+                    R.color.desktop_mode_caption_button_color_selector_dark)
+            } else {
+                ContextCompat.getColorStateList(context,
+                    R.color.desktop_mode_caption_button_color_selector_light)
+            }
+            stubProgressBarContainer.setOnInflateListener { _, inflated ->
+                val progressBar = (inflated as FrameLayout)
+                    .requireViewById(R.id.progress_bar) as ProgressBar
+                progressBar.progressTintList = progressTint
+            }
+            maximizeWindow.background?.setTintList(backgroundTint)
         }
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt
index 11a7cf8..cc54d25 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt
@@ -63,7 +63,7 @@
 import com.android.wm.shell.RootTaskDisplayAreaOrganizer
 import com.android.wm.shell.common.DisplayController
 import com.android.wm.shell.common.SyncTransactionQueue
-import com.android.wm.shell.desktopmode.calculateMaximizeBounds
+import com.android.wm.shell.desktopmode.isTaskMaximized
 import com.android.wm.shell.shared.animation.Interpolators.EMPHASIZED_DECELERATE
 import com.android.wm.shell.shared.animation.Interpolators.FAST_OUT_LINEAR_IN
 import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalViewHostViewContainer
@@ -231,11 +231,7 @@
     }
 
     private fun getSizeToggleDirection(): MaximizeMenuView.SizeToggleDirection {
-        val maximizeBounds = calculateMaximizeBounds(
-            displayController.getDisplayLayout(taskInfo.displayId)!!,
-            taskInfo
-        )
-        val maximized = taskInfo.configuration.windowConfiguration.bounds.equals(maximizeBounds)
+        val maximized = isTaskMaximized(taskInfo, displayController)
         return if (maximized)
             MaximizeMenuView.SizeToggleDirection.RESTORE
         else
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.kt
index 8770d35..96839ce 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.kt
@@ -20,7 +20,6 @@
 import android.animation.ValueAnimator
 import android.app.ActivityManager.RunningTaskInfo
 import android.content.Context
-import android.graphics.Bitmap
 import android.graphics.Color
 import android.graphics.PixelFormat
 import android.graphics.PointF
@@ -38,13 +37,23 @@
 import androidx.compose.material3.dynamicDarkColorScheme
 import androidx.compose.material3.dynamicLightColorScheme
 import androidx.compose.ui.graphics.toArgb
+import com.android.internal.annotations.VisibleForTesting
 import com.android.wm.shell.R
 import com.android.wm.shell.common.DisplayController
 import com.android.wm.shell.common.DisplayController.OnDisplaysChangedListener
+import com.android.wm.shell.shared.annotations.ShellBackgroundThread
+import com.android.wm.shell.shared.annotations.ShellMainThread
 import com.android.wm.shell.windowdecor.WindowDecoration.SurfaceControlViewHostFactory
+import com.android.wm.shell.windowdecor.common.WindowDecorTaskResourceLoader
 import com.android.wm.shell.windowdecor.common.DecorThemeUtil
 import com.android.wm.shell.windowdecor.common.Theme
 import java.util.function.Supplier
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.isActive
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
 
 /**
  * Creates and updates a veil that covers task contents on resize.
@@ -52,7 +61,9 @@
 public class ResizeVeil @JvmOverloads constructor(
         private val context: Context,
         private val displayController: DisplayController,
-        private val appIcon: Bitmap,
+        private val taskResourceLoader: WindowDecorTaskResourceLoader,
+        @ShellMainThread private val mainDispatcher: CoroutineDispatcher,
+        @ShellBackgroundThread private val bgScope: CoroutineScope,
         private var parentSurface: SurfaceControl,
         private val surfaceControlTransactionSupplier: Supplier<SurfaceControl.Transaction>,
         private val surfaceControlBuilderFactory: SurfaceControlBuilderFactory =
@@ -65,7 +76,8 @@
     private val lightColors = dynamicLightColorScheme(context)
     private val darkColors = dynamicDarkColorScheme(context)
 
-    private lateinit var iconView: ImageView
+    @VisibleForTesting
+    lateinit var iconView: ImageView
     private var iconSize = 0
 
     /** A container surface to host the veil background and icon child surfaces.  */
@@ -77,6 +89,7 @@
     private var viewHost: SurfaceControlViewHost? = null
     private var display: Display? = null
     private var veilAnimator: ValueAnimator? = null
+    private var loadAppInfoJob: Job? = null
 
     /**
      * Whether the resize veil is currently visible.
@@ -142,7 +155,6 @@
         val root = LayoutInflater.from(context)
                 .inflate(R.layout.desktop_mode_resize_veil, null /* root */)
         iconView = root.requireViewById(R.id.veil_application_icon)
-        iconView.setImageBitmap(appIcon)
         val lp = WindowManager.LayoutParams(
                 iconSize,
                 iconSize,
@@ -156,6 +168,14 @@
                 iconSurface, null /* hostInputToken */)
         viewHost = surfaceControlViewHostFactory.create(context, display, wwm, "ResizeVeil")
         viewHost?.setView(root, lp)
+        loadAppInfoJob = bgScope.launch {
+            if (!isActive) return@launch
+            val icon = taskResourceLoader.getVeilIcon(taskInfo)
+            withContext(mainDispatcher) {
+                if (!isActive) return@withContext
+                iconView.setImageBitmap(icon)
+            }
+        }
         Trace.endSection()
     }
 
@@ -401,6 +421,7 @@
         cancelAnimation()
         veilAnimator = null
         isVisible = false
+        loadAppInfoJob?.cancel()
 
         viewHost?.release()
         viewHost = null
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java
index 1f03d75..e011cc0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java
@@ -104,7 +104,7 @@
     }
 
     @Override
-    public Rect onDragPositioningStart(int ctrlType, float x, float y) {
+    public Rect onDragPositioningStart(int ctrlType, int displayId, float x, float y) {
         mCtrlType = ctrlType;
         mTaskBoundsAtDragStart.set(
                 mDesktopWindowDecoration.mTaskInfo.configuration.windowConfiguration.getBounds());
@@ -136,7 +136,7 @@
     }
 
     @Override
-    public Rect onDragPositioningMove(float x, float y) {
+    public Rect onDragPositioningMove(int displayId, float x, float y) {
         if (Looper.myLooper() != mHandler.getLooper()) {
             // This method must run on the shell main thread to use the correct Choreographer
             // instance below.
@@ -170,7 +170,7 @@
     }
 
     @Override
-    public Rect onDragPositioningEnd(float x, float y) {
+    public Rect onDragPositioningEnd(int displayId, float x, float y) {
         PointF delta = DragPositioningCallbackUtility.calculateDelta(x, y,
                 mRepositionStartPoint);
         if (isResizing()) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/WindowDecorTaskResourceLoader.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/WindowDecorTaskResourceLoader.kt
new file mode 100644
index 0000000..d87da09
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/WindowDecorTaskResourceLoader.kt
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.common
+
+import android.annotation.DimenRes
+import android.app.ActivityManager
+import android.app.ActivityManager.RunningTaskInfo
+import android.content.Context
+import android.content.pm.ActivityInfo
+import android.content.pm.PackageManager
+import android.graphics.Bitmap
+import android.os.UserHandle
+import androidx.tracing.Trace
+import com.android.internal.annotations.VisibleForTesting
+import com.android.launcher3.icons.BaseIconFactory
+import com.android.launcher3.icons.BaseIconFactory.MODE_DEFAULT
+import com.android.launcher3.icons.IconProvider
+import com.android.wm.shell.R
+import com.android.wm.shell.shared.annotations.ShellBackgroundThread
+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.sysui.UserChangeListener
+import java.io.PrintWriter
+import java.util.concurrent.ConcurrentHashMap
+
+/**
+ * A utility and cache for window decoration UI resources.
+ */
+class WindowDecorTaskResourceLoader(
+    private val context: Context,
+    shellInit: ShellInit,
+    private val shellController: ShellController,
+    private val shellCommandHandler: ShellCommandHandler,
+    private val iconProvider: IconProvider,
+    private val headerIconFactory: BaseIconFactory,
+    private val veilIconFactory: BaseIconFactory,
+) {
+    constructor(
+        context: Context,
+        shellInit: ShellInit,
+        shellController: ShellController,
+        shellCommandHandler: ShellCommandHandler,
+    ) : this(
+        context,
+        shellInit,
+        shellController,
+        shellCommandHandler,
+        IconProvider(context),
+        headerIconFactory = context.createIconFactory(R.dimen.desktop_mode_caption_icon_radius),
+        veilIconFactory = context.createIconFactory(R.dimen.desktop_mode_resize_veil_icon_size),
+    )
+
+    /**
+     * A map of task -> resources to prevent unnecessary binder calls and resource loading
+     * when multiple window decorations need the same resources, for example, the app name or icon
+     * used in the header and menu.
+     */
+    @VisibleForTesting
+    val taskToResourceCache = ConcurrentHashMap<Int, AppResources>()
+    /**
+     * Keeps track of existing tasks with a window decoration. Useful to verify that requests to
+     * get resources occur within the lifecycle of a window decoration, otherwise it'd be possible
+     * to load a tasks resources into memory without a future signal to clean up the resource.
+     * See [onWindowDecorClosed].
+     */
+    private val existingTasks = mutableSetOf<Int>()
+
+    @VisibleForTesting
+    lateinit var currentUserContext: Context
+
+    init {
+        shellInit.addInitCallback(this::onInit, this)
+    }
+
+    private fun onInit() {
+        shellCommandHandler.addDumpCallback(this::dump, this)
+        shellController.addUserChangeListener(object : UserChangeListener {
+            override fun onUserChanged(newUserId: Int, userContext: Context) {
+                currentUserContext = userContext
+                // No need to hold on to resources for tasks of another profile.
+                taskToResourceCache.clear()
+            }
+        })
+        currentUserContext = context.createContextAsUser(
+            UserHandle.of(ActivityManager.getCurrentUser()), /* flags= */ 0
+        )
+    }
+
+    /** Returns the user readable name for this task. */
+    @ShellBackgroundThread
+    fun getName(taskInfo: RunningTaskInfo): CharSequence {
+        checkWindowDecorExists(taskInfo)
+        val cachedResources = taskToResourceCache[taskInfo.taskId]
+        if (cachedResources != null) {
+            return cachedResources.appName
+        }
+        val resources = loadAppResources(taskInfo)
+        taskToResourceCache[taskInfo.taskId] = resources
+        return resources.appName
+    }
+
+    /** Returns the icon for use by the app header and menus for this task. */
+    @ShellBackgroundThread
+    fun getHeaderIcon(taskInfo: RunningTaskInfo): Bitmap {
+        checkWindowDecorExists(taskInfo)
+        val cachedResources = taskToResourceCache[taskInfo.taskId]
+        if (cachedResources != null) {
+            return cachedResources.appIcon
+        }
+        val resources = loadAppResources(taskInfo)
+        taskToResourceCache[taskInfo.taskId] = resources
+        return resources.appIcon
+    }
+
+    /** Returns the icon for use by the resize veil for this task. */
+    @ShellBackgroundThread
+    fun getVeilIcon(taskInfo: RunningTaskInfo): Bitmap {
+        checkWindowDecorExists(taskInfo)
+        val cachedResources = taskToResourceCache[taskInfo.taskId]
+        if (cachedResources != null) {
+            return cachedResources.veilIcon
+        }
+        val resources = loadAppResources(taskInfo)
+        taskToResourceCache[taskInfo.taskId] = resources
+        return resources.veilIcon
+    }
+
+    /** Called when a window decoration for this task is created. */
+    fun onWindowDecorCreated(taskInfo: RunningTaskInfo) {
+        existingTasks.add(taskInfo.taskId)
+    }
+
+    /** Called when a window decoration for this task is closed. */
+    fun onWindowDecorClosed(taskInfo: RunningTaskInfo) {
+        existingTasks.remove(taskInfo.taskId)
+        taskToResourceCache.remove(taskInfo.taskId)
+    }
+
+    private fun checkWindowDecorExists(taskInfo: RunningTaskInfo) {
+        check(existingTasks.contains(taskInfo.taskId)) {
+            "Attempt to obtain resource for non-existent decoration"
+        }
+    }
+
+    private fun loadAppResources(taskInfo: RunningTaskInfo): AppResources {
+        Trace.beginSection("$TAG#loadAppResources")
+        val pm = currentUserContext.packageManager
+        val activityInfo = getActivityInfo(taskInfo, pm)
+        val appName = pm.getApplicationLabel(activityInfo.applicationInfo)
+        val appIconDrawable = iconProvider.getIcon(activityInfo)
+        val badgedAppIconDrawable = pm.getUserBadgedIcon(appIconDrawable, taskInfo.userHandle())
+        val appIcon = headerIconFactory.createIconBitmap(badgedAppIconDrawable, /* scale= */ 1f)
+        val veilIcon = veilIconFactory.createScaledBitmap(appIconDrawable, MODE_DEFAULT)
+        Trace.endSection()
+        return AppResources(appName = appName, appIcon = appIcon, veilIcon = veilIcon)
+    }
+
+    private fun getActivityInfo(taskInfo: RunningTaskInfo, pm: PackageManager): ActivityInfo {
+        return pm.getActivityInfo(taskInfo.component(), /* flags= */ 0)
+    }
+
+    private fun RunningTaskInfo.component() = baseIntent.component!!
+
+    private fun RunningTaskInfo.userHandle() = UserHandle.of(userId)
+
+    data class AppResources(val appName: CharSequence, val appIcon: Bitmap, val veilIcon: Bitmap)
+
+    private fun dump(pw: PrintWriter, prefix: String) {
+        val innerPrefix = "$prefix  "
+        pw.println("${prefix}$TAG")
+        pw.println(innerPrefix + "appResourceCache=$taskToResourceCache")
+        pw.println(innerPrefix + "existingTasks=$existingTasks")
+    }
+
+    companion object {
+        private const val TAG = "AppResourceProvider"
+    }
+}
+
+/** Creates an icon factory with the provided [dimensions]. */
+fun Context.createIconFactory(@DimenRes dimensions: Int): BaseIconFactory {
+    val densityDpi = resources.displayMetrics.densityDpi
+    val iconSize = resources.getDimensionPixelSize(dimensions)
+    return BaseIconFactory(this, densityDpi, iconSize)
+}
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 9db69d5..d72da3a 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
@@ -31,17 +31,23 @@
 import com.android.wm.shell.common.DisplayController
 import com.android.wm.shell.common.SyncTransactionQueue
 import com.android.wm.shell.desktopmode.DesktopModeEventLogger
-import com.android.wm.shell.desktopmode.DesktopRepository
 import com.android.wm.shell.desktopmode.DesktopTasksController
 import com.android.wm.shell.desktopmode.DesktopUserRepositories
 import com.android.wm.shell.desktopmode.ReturnToDragStartAnimator
 import com.android.wm.shell.desktopmode.ToggleResizeDesktopTaskTransitionHandler
+import com.android.wm.shell.shared.annotations.ShellBackgroundThread
+import com.android.wm.shell.shared.annotations.ShellMainThread
 import com.android.wm.shell.transition.Transitions
 import com.android.wm.shell.windowdecor.DesktopModeWindowDecoration
+import com.android.wm.shell.windowdecor.common.WindowDecorTaskResourceLoader
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.MainCoroutineDispatcher
 
 /** Manages tiling for each displayId/userId independently. */
 class DesktopTilingDecorViewModel(
     private val context: Context,
+    @ShellMainThread private val mainDispatcher: MainCoroutineDispatcher,
+    @ShellBackgroundThread private val bgScope: CoroutineScope,
     private val displayController: DisplayController,
     private val rootTdaOrganizer: RootTaskDisplayAreaOrganizer,
     private val syncQueue: SyncTransactionQueue,
@@ -51,6 +57,7 @@
     private val returnToDragStartAnimator: ReturnToDragStartAnimator,
     private val desktopUserRepositories: DesktopUserRepositories,
     private val desktopModeEventLogger: DesktopModeEventLogger,
+    private val taskResourceLoader: WindowDecorTaskResourceLoader,
 ) : DisplayChangeController.OnDisplayChangingListener {
     @VisibleForTesting
     var tilingTransitionHandlerByDisplayId = SparseArray<DesktopTilingWindowDecoration>()
@@ -74,8 +81,11 @@
                     val newHandler =
                         DesktopTilingWindowDecoration(
                             context,
+                            mainDispatcher,
+                            bgScope,
                             syncQueue,
                             displayController,
+                            taskResourceLoader,
                             displayId,
                             rootTdaOrganizer,
                             transitions,
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 7ceac52..6f23233 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
@@ -20,11 +20,9 @@
 import android.content.Context
 import android.content.res.Configuration
 import android.content.res.Resources
-import android.graphics.Bitmap
 import android.graphics.Rect
 import android.os.IBinder
 import android.os.UserHandle
-import android.util.Slog
 import android.view.MotionEvent
 import android.view.SurfaceControl
 import android.view.SurfaceControl.Transaction
@@ -37,8 +35,6 @@
 import android.window.WindowContainerTransaction
 import com.android.internal.annotations.VisibleForTesting
 import com.android.launcher3.icons.BaseIconFactory
-import com.android.launcher3.icons.BaseIconFactory.MODE_DEFAULT
-import com.android.launcher3.icons.IconProvider
 import com.android.wm.shell.R
 import com.android.wm.shell.RootTaskDisplayAreaOrganizer
 import com.android.wm.shell.ShellTaskOrganizer
@@ -47,11 +43,12 @@
 import com.android.wm.shell.common.SyncTransactionQueue
 import com.android.wm.shell.desktopmode.DesktopModeEventLogger
 import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ResizeTrigger
-import com.android.wm.shell.desktopmode.DesktopRepository
 import com.android.wm.shell.desktopmode.DesktopTasksController.SnapPosition
 import com.android.wm.shell.desktopmode.DesktopUserRepositories
 import com.android.wm.shell.desktopmode.ReturnToDragStartAnimator
 import com.android.wm.shell.desktopmode.ToggleResizeDesktopTaskTransitionHandler
+import com.android.wm.shell.shared.annotations.ShellBackgroundThread
+import com.android.wm.shell.shared.annotations.ShellMainThread
 import com.android.wm.shell.transition.Transitions
 import com.android.wm.shell.transition.Transitions.TRANSIT_MINIMIZE
 import com.android.wm.shell.windowdecor.DesktopModeWindowDecoration
@@ -60,13 +57,19 @@
 import com.android.wm.shell.windowdecor.DragResizeWindowGeometry
 import com.android.wm.shell.windowdecor.DragResizeWindowGeometry.DisabledEdge.NONE
 import com.android.wm.shell.windowdecor.ResizeVeil
+import com.android.wm.shell.windowdecor.common.WindowDecorTaskResourceLoader
 import com.android.wm.shell.windowdecor.extension.isFullscreen
 import java.util.function.Supplier
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.MainCoroutineDispatcher
 
 class DesktopTilingWindowDecoration(
     private var context: Context,
+    @ShellMainThread private val mainDispatcher: MainCoroutineDispatcher,
+    @ShellBackgroundThread private val bgScope: CoroutineScope,
     private val syncQueue: SyncTransactionQueue,
     private val displayController: DisplayController,
+    private val taskResourceLoader: WindowDecorTaskResourceLoader,
     private val displayId: Int,
     private val rootTdaOrganizer: RootTaskDisplayAreaOrganizer,
     private val transitions: Transitions,
@@ -110,6 +113,9 @@
                 context,
                 destinationBounds,
                 displayController,
+                taskResourceLoader,
+                mainDispatcher,
+                bgScope,
                 transactionSupplier,
             )
         val isFirstTiledApp = leftTaskResizingHelper == null && rightTaskResizingHelper == null
@@ -408,11 +414,13 @@
         val context: Context,
         val bounds: Rect,
         val displayController: DisplayController,
+        private val taskResourceLoader: WindowDecorTaskResourceLoader,
+        @ShellMainThread val mainDispatcher: MainCoroutineDispatcher,
+        @ShellBackgroundThread val bgScope: CoroutineScope,
         val transactionSupplier: Supplier<Transaction>,
     ) {
         var isInitialised = false
         var newBounds = Rect(bounds)
-        private lateinit var resizeVeilBitmap: Bitmap
         private lateinit var resizeVeil: ResizeVeil
         private val displayContext = displayController.getDisplayContext(taskInfo.displayId)
         private val userContext =
@@ -426,26 +434,14 @@
         }
 
         private fun initVeil() {
-            val baseActivity = taskInfo.baseActivity
-            if (baseActivity == null) {
-                Slog.e(TAG, "Base activity component not found in task")
-                return
-            }
-            val resizeVeilIconFactory =
-                displayContext?.let {
-                    createIconFactory(displayContext, R.dimen.desktop_mode_resize_veil_icon_size)
-                } ?: return
-            val pm = userContext.getPackageManager()
-            val activityInfo = pm.getActivityInfo(baseActivity, 0 /* flags */)
-            val provider = IconProvider(displayContext)
-            val appIconDrawable = provider.getIcon(activityInfo)
-            resizeVeilBitmap =
-                resizeVeilIconFactory.createScaledBitmap(appIconDrawable, MODE_DEFAULT)
+            displayContext ?: return
             resizeVeil =
                 ResizeVeil(
                     context = displayContext,
                     displayController = displayController,
-                    appIcon = resizeVeilBitmap,
+                    taskResourceLoader = taskResourceLoader,
+                    mainDispatcher = mainDispatcher,
+                    bgScope = bgScope,
                     parentSurface = desktopModeWindowDecoration.getLeash(),
                     surfaceControlTransactionSupplier = transactionSupplier,
                     taskInfo = taskInfo,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHeaderViewHolder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHeaderViewHolder.kt
index f3a8b20..dc4fa37 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHeaderViewHolder.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHeaderViewHolder.kt
@@ -72,8 +72,6 @@
         onCaptionButtonClickListener: View.OnClickListener,
         private val onLongClickListener: OnLongClickListener,
         onCaptionGenericMotionListener: View.OnGenericMotionListener,
-        appName: CharSequence,
-        appIconBitmap: Bitmap,
         onMaximizeHoverAnimationFinishedListener: () -> Unit
 ) : WindowDecorationViewHolder<AppHeaderViewHolder.HeaderData>(rootView) {
 
@@ -154,8 +152,6 @@
         closeWindowButton.setOnTouchListener(onCaptionTouchListener)
         minimizeWindowButton.setOnClickListener(onCaptionButtonClickListener)
         minimizeWindowButton.setOnTouchListener(onCaptionTouchListener)
-        appNameTextView.text = appName
-        appIconImageView.setImageBitmap(appIconBitmap)
         maximizeButtonView.onHoverAnimationFinishedListener =
                 onMaximizeHoverAnimationFinishedListener
     }
@@ -170,6 +166,16 @@
         )
     }
 
+    /** Sets the app's name in the header. */
+    fun setAppName(name: CharSequence) {
+        appNameTextView.text = name
+    }
+
+    /** Sets the app's icon in the header. */
+    fun setAppIcon(icon: Bitmap) {
+        appIconImageView.setImageBitmap(icon)
+    }
+
     private fun bindData(
         taskInfo: RunningTaskInfo,
         isTaskMaximized: Boolean,
@@ -628,8 +634,6 @@
             onCaptionButtonClickListener: View.OnClickListener,
             onLongClickListener: OnLongClickListener,
             onCaptionGenericMotionListener: View.OnGenericMotionListener,
-            appName: CharSequence,
-            appIconBitmap: Bitmap,
             onMaximizeHoverAnimationFinishedListener: () -> Unit,
         ): AppHeaderViewHolder = AppHeaderViewHolder(
             rootView,
@@ -637,8 +641,6 @@
             onCaptionButtonClickListener,
             onLongClickListener,
             onCaptionGenericMotionListener,
-            appName,
-            appIconBitmap,
             onMaximizeHoverAnimationFinishedListener,
         )
     }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/pip/PipAppOpsListenerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/pip/PipAppOpsListenerTest.java
index b9490b8..e92e243 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/pip/PipAppOpsListenerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/pip/PipAppOpsListenerTest.java
@@ -78,7 +78,8 @@
     @Test
     public void onActivityPinned_registerAppOpsListener() {
         String packageName = "com.android.test.pip";
-        mPipAppOpsListener = new PipAppOpsListener(mMockContext, mMockCallback, mMockExecutor);
+        mPipAppOpsListener = new PipAppOpsListener(mMockContext, mMockExecutor);
+        mPipAppOpsListener.setCallback(mMockCallback);
 
         mPipAppOpsListener.onActivityPinned(packageName);
 
@@ -89,7 +90,8 @@
 
     @Test
     public void onActivityUnpinned_unregisterAppOpsListener() {
-        mPipAppOpsListener = new PipAppOpsListener(mMockContext, mMockCallback, mMockExecutor);
+        mPipAppOpsListener = new PipAppOpsListener(mMockContext, mMockExecutor);
+        mPipAppOpsListener.setCallback(mMockCallback);
 
         mPipAppOpsListener.onActivityUnpinned();
 
@@ -99,7 +101,8 @@
     @Test
     public void disablePipAppOps_dismissPip() throws PackageManager.NameNotFoundException {
         String packageName = "com.android.test.pip";
-        mPipAppOpsListener = new PipAppOpsListener(mMockContext, mMockCallback, mMockExecutor);
+        mPipAppOpsListener = new PipAppOpsListener(mMockContext, mMockExecutor);
+        mPipAppOpsListener.setCallback(mMockCallback);
         // Set up the top pip activity info as mTopPipActivity
         mTopPipActivity = new Pair<>(new ComponentName(packageName, "PipActivity"), 0);
         mPipAppOpsListener.setTopPipActivityInfoSupplier(this::getTopPipActivity);
@@ -131,7 +134,8 @@
     public void disablePipAppOps_differentPackage_doNothing()
             throws PackageManager.NameNotFoundException {
         String packageName = "com.android.test.pip";
-        mPipAppOpsListener = new PipAppOpsListener(mMockContext, mMockCallback, mMockExecutor);
+        mPipAppOpsListener = new PipAppOpsListener(mMockContext, mMockExecutor);
+        mPipAppOpsListener.setCallback(mMockCallback);
         // Set up the top pip activity info as mTopPipActivity
         mTopPipActivity = new Pair<>(new ComponentName(packageName, "PipActivity"), 0);
         mPipAppOpsListener.setTopPipActivityInfoSupplier(this::getTopPipActivity);
@@ -160,7 +164,8 @@
     public void disablePipAppOps_nameNotFound_unregisterAppOpsListener()
             throws PackageManager.NameNotFoundException {
         String packageName = "com.android.test.pip";
-        mPipAppOpsListener = new PipAppOpsListener(mMockContext, mMockCallback, mMockExecutor);
+        mPipAppOpsListener = new PipAppOpsListener(mMockContext, mMockExecutor);
+        mPipAppOpsListener.setCallback(mMockCallback);
         // Set up the top pip activity info as mTopPipActivity
         mTopPipActivity = new Pair<>(new ComponentName(packageName, "PipActivity"), 0);
         mPipAppOpsListener.setTopPipActivityInfoSupplier(this::getTopPipActivity);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/AppCompatUtilsTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/AppCompatUtilsTest.kt
index d52fd4f..7157a7f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/AppCompatUtilsTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/AppCompatUtilsTest.kt
@@ -20,7 +20,6 @@
 import android.testing.AndroidTestingRunner
 import androidx.test.filters.SmallTest
 import com.android.internal.R
-import com.android.wm.shell.ShellTestCase
 import com.android.wm.shell.desktopmode.DesktopTestHelpers.createFreeformTask
 import org.junit.Assert.assertFalse
 import org.junit.Assert.assertTrue
@@ -28,14 +27,13 @@
 import org.junit.runner.RunWith
 
 /**
- * Tests for {@link AppCompatUtils}.
+ * Tests for [@link AppCompatUtils].
  *
- * Build/Install/Run:
- *  atest WMShellUnitTests:AppCompatUtilsTest
+ * Build/Install/Run: atest WMShellUnitTests:AppCompatUtilsTest
  */
 @RunWith(AndroidTestingRunner::class)
 @SmallTest
-class AppCompatUtilsTest : ShellTestCase() {
+class AppCompatUtilsTest : CompatUIShellTestCase() {
     @Test
     fun testIsTopActivityExemptFromDesktopWindowing_onlyTransparentActivitiesInStack() {
         assertTrue(isTopActivityExemptFromDesktopWindowing(mContext,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
index 67573da..ecf766d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
@@ -39,9 +39,6 @@
 import android.platform.test.annotations.DisableFlags;
 import android.platform.test.annotations.EnableFlags;
 import android.platform.test.annotations.RequiresFlagsDisabled;
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.DeviceFlagsValueProvider;
-import android.platform.test.flag.junit.SetFlagsRule;
 import android.testing.AndroidTestingRunner;
 import android.view.InsetsSource;
 import android.view.InsetsState;
@@ -52,7 +49,6 @@
 
 import com.android.window.flags.Flags;
 import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.ShellTestCase;
 import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.common.DisplayImeController;
 import com.android.wm.shell.common.DisplayInsetsController;
@@ -70,11 +66,8 @@
 
 import dagger.Lazy;
 
-import java.util.Optional;
-
 import org.junit.Assert;
 import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
@@ -82,25 +75,20 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.util.Optional;
+
 /**
  * Tests for {@link CompatUIController}.
  *
  * Build/Install/Run:
- *  atest WMShellUnitTests:CompatUIControllerTest
+ * atest WMShellUnitTests:CompatUIControllerTest
  */
 @RunWith(AndroidTestingRunner.class)
 @SmallTest
-public class CompatUIControllerTest extends ShellTestCase {
+public class CompatUIControllerTest extends CompatUIShellTestCase {
     private static final int DISPLAY_ID = 0;
     private static final int TASK_ID = 12;
 
-    @Rule
-    public final CheckFlagsRule mCheckFlagsRule =
-            DeviceFlagsValueProvider.createCheckFlagsRule();
-
-    @Rule
-    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
-
     private CompatUIController mController;
     private ShellInit mShellInit;
     @Mock
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java
index e5d1919..2117b06 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java
@@ -26,8 +26,6 @@
 import android.app.TaskInfo;
 import android.graphics.Rect;
 import android.platform.test.annotations.RequiresFlagsDisabled;
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.DeviceFlagsValueProvider;
 import android.testing.AndroidTestingRunner;
 import android.util.Pair;
 import android.view.LayoutInflater;
@@ -40,7 +38,6 @@
 import com.android.window.flags.Flags;
 import com.android.wm.shell.R;
 import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.ShellTestCase;
 import com.android.wm.shell.common.DisplayLayout;
 import com.android.wm.shell.common.SyncTransactionQueue;
 import com.android.wm.shell.compatui.CompatUIController.CompatUIHintsState;
@@ -49,7 +46,6 @@
 import junit.framework.Assert;
 
 import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
@@ -66,14 +62,10 @@
  */
 @RunWith(AndroidTestingRunner.class)
 @SmallTest
-public class CompatUILayoutTest extends ShellTestCase {
+public class CompatUILayoutTest extends CompatUIShellTestCase {
 
     private static final int TASK_ID = 1;
 
-    @Rule
-    public final CheckFlagsRule mCheckFlagsRule =
-            DeviceFlagsValueProvider.createCheckFlagsRule();
-
     @Mock private SyncTransactionQueue mSyncTransactionQueue;
     @Mock private Consumer<CompatUIEvent> mCallback;
     @Mock private Consumer<Pair<TaskInfo, ShellTaskOrganizer.TaskListener>> mOnRestartButtonClicked;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIShellTestCase.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIShellTestCase.java
new file mode 100644
index 0000000..5a49d01
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIShellTestCase.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.compatui;
+
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.platform.test.flag.junit.SetFlagsRule;
+
+import com.android.wm.shell.ShellTestCase;
+
+import org.junit.Rule;
+
+/**
+ * Base class for CompatUI tests.
+ */
+public class CompatUIShellTestCase extends ShellTestCase {
+
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule =
+            DeviceFlagsValueProvider.createCheckFlagsRule();
+
+    @Rule
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIStatusManagerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIStatusManagerTest.java
index 8fd7c0e..0b37648 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIStatusManagerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIStatusManagerTest.java
@@ -27,8 +27,6 @@
 
 import androidx.test.filters.SmallTest;
 
-import com.android.wm.shell.ShellTestCase;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -44,7 +42,7 @@
  */
 @RunWith(AndroidTestingRunner.class)
 @SmallTest
-public class CompatUIStatusManagerTest extends ShellTestCase {
+public class CompatUIStatusManagerTest extends CompatUIShellTestCase {
 
     private FakeCompatUIStatusManagerTest mTestState;
     private CompatUIStatusManager mStatusManager;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java
index 1c01756..61b6d80 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java
@@ -16,7 +16,6 @@
 
 package com.android.wm.shell.compatui;
 
-import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
 import static android.view.WindowInsets.Type.navigationBars;
 import static android.view.WindowManager.LARGE_SCREEN_SMALLEST_SCREEN_WIDTH_DP;
 
@@ -40,9 +39,6 @@
 import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.platform.test.annotations.RequiresFlagsDisabled;
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.DeviceFlagsValueProvider;
-import android.platform.test.flag.junit.SetFlagsRule;
 import android.testing.AndroidTestingRunner;
 import android.util.Pair;
 import android.view.DisplayInfo;
@@ -56,7 +52,6 @@
 
 import com.android.window.flags.Flags;
 import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.ShellTestCase;
 import com.android.wm.shell.common.DisplayLayout;
 import com.android.wm.shell.common.SyncTransactionQueue;
 import com.android.wm.shell.compatui.CompatUIController.CompatUIHintsState;
@@ -65,7 +60,6 @@
 import junit.framework.Assert;
 
 import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
@@ -82,13 +76,7 @@
  */
 @RunWith(AndroidTestingRunner.class)
 @SmallTest
-public class CompatUIWindowManagerTest extends ShellTestCase {
-    @Rule
-    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT);
-
-    @Rule
-    public final CheckFlagsRule mCheckFlagsRule =
-            DeviceFlagsValueProvider.createCheckFlagsRule();
+public class CompatUIWindowManagerTest extends CompatUIShellTestCase {
 
     private static final int TASK_ID = 1;
     private static final int TASK_WIDTH = 2000;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/LetterboxEduDialogLayoutTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/LetterboxEduDialogLayoutTest.java
index e8191db..e786fef 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/LetterboxEduDialogLayoutTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/LetterboxEduDialogLayoutTest.java
@@ -25,8 +25,6 @@
 import static org.mockito.Mockito.verify;
 
 import android.platform.test.annotations.RequiresFlagsDisabled;
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.DeviceFlagsValueProvider;
 import android.testing.AndroidTestingRunner;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -34,10 +32,8 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.wm.shell.R;
-import com.android.wm.shell.ShellTestCase;
 
 import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
@@ -51,7 +47,7 @@
  */
 @RunWith(AndroidTestingRunner.class)
 @SmallTest
-public class LetterboxEduDialogLayoutTest extends ShellTestCase {
+public class LetterboxEduDialogLayoutTest extends CompatUIShellTestCase {
 
     @Mock
     private Runnable mDismissCallback;
@@ -60,10 +56,6 @@
     private View mDismissButton;
     private View mDialogContainer;
 
-    @Rule
-    public final CheckFlagsRule mCheckFlagsRule =
-            DeviceFlagsValueProvider.createCheckFlagsRule();
-
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/LetterboxEduWindowManagerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/LetterboxEduWindowManagerTest.java
index 4c97c76..09fc082 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/LetterboxEduWindowManagerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/LetterboxEduWindowManagerTest.java
@@ -46,9 +46,6 @@
 import android.platform.test.annotations.DisableFlags;
 import android.platform.test.annotations.EnableFlags;
 import android.platform.test.annotations.RequiresFlagsDisabled;
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.DeviceFlagsValueProvider;
-import android.platform.test.flag.junit.SetFlagsRule;
 import android.testing.AndroidTestingRunner;
 import android.util.Pair;
 import android.view.DisplayCutout;
@@ -65,7 +62,6 @@
 import com.android.window.flags.Flags;
 import com.android.wm.shell.R;
 import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.ShellTestCase;
 import com.android.wm.shell.TestShellExecutor;
 import com.android.wm.shell.common.DisplayLayout;
 import com.android.wm.shell.common.DockStateReader;
@@ -75,7 +71,6 @@
 
 import org.junit.After;
 import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
@@ -95,7 +90,7 @@
  */
 @RunWith(AndroidTestingRunner.class)
 @SmallTest
-public class LetterboxEduWindowManagerTest extends ShellTestCase {
+public class LetterboxEduWindowManagerTest extends CompatUIShellTestCase {
 
     private static final int USER_ID_1 = 1;
     private static final int USER_ID_2 = 2;
@@ -128,18 +123,11 @@
     @Mock private Consumer<Pair<TaskInfo, ShellTaskOrganizer.TaskListener>> mOnDismissCallback;
     @Mock private DockStateReader mDockStateReader;
 
-    @Rule
-    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
-
     private CompatUIConfiguration mCompatUIConfiguration;
     private TestShellExecutor mExecutor;
     private FakeCompatUIStatusManagerTest mCompatUIStatus;
     private CompatUIStatusManager mCompatUIStatusManager;
 
-    @Rule
-    public final CheckFlagsRule mCheckFlagsRule =
-            DeviceFlagsValueProvider.createCheckFlagsRule();
-
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/ReachabilityEduLayoutTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/ReachabilityEduLayoutTest.java
index 0da14d6..02c099b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/ReachabilityEduLayoutTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/ReachabilityEduLayoutTest.java
@@ -26,8 +26,6 @@
 
 import android.app.TaskInfo;
 import android.platform.test.annotations.RequiresFlagsDisabled;
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.DeviceFlagsValueProvider;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.view.LayoutInflater;
@@ -36,10 +34,8 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.wm.shell.R;
-import com.android.wm.shell.ShellTestCase;
 
 import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
@@ -54,7 +50,7 @@
 @RunWith(AndroidTestingRunner.class)
 @SmallTest
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
-public class ReachabilityEduLayoutTest extends ShellTestCase {
+public class ReachabilityEduLayoutTest extends CompatUIShellTestCase {
 
     private ReachabilityEduLayout mLayout;
     private View mMoveUpButton;
@@ -68,10 +64,6 @@
     @Mock
     private TaskInfo mTaskInfo;
 
-    @Rule
-    public final CheckFlagsRule mCheckFlagsRule =
-            DeviceFlagsValueProvider.createCheckFlagsRule();
-
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/ReachabilityEduWindowManagerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/ReachabilityEduWindowManagerTest.java
index eafb414..fa04e07 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/ReachabilityEduWindowManagerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/ReachabilityEduWindowManagerTest.java
@@ -25,14 +25,11 @@
 import android.app.TaskInfo;
 import android.content.res.Configuration;
 import android.platform.test.annotations.RequiresFlagsDisabled;
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.DeviceFlagsValueProvider;
 import android.testing.AndroidTestingRunner;
 
 import androidx.test.filters.SmallTest;
 
 import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.ShellTestCase;
 import com.android.wm.shell.TestShellExecutor;
 import com.android.wm.shell.common.DisplayLayout;
 import com.android.wm.shell.common.SyncTransactionQueue;
@@ -40,7 +37,6 @@
 import junit.framework.Assert;
 
 import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
@@ -56,7 +52,7 @@
  */
 @RunWith(AndroidTestingRunner.class)
 @SmallTest
-public class ReachabilityEduWindowManagerTest extends ShellTestCase {
+public class ReachabilityEduWindowManagerTest extends CompatUIShellTestCase {
     @Mock
     private SyncTransactionQueue mSyncTransactionQueue;
     @Mock
@@ -71,10 +67,6 @@
     private TaskInfo mTaskInfo;
     private ReachabilityEduWindowManager mWindowManager;
 
-    @Rule
-    public final CheckFlagsRule mCheckFlagsRule =
-            DeviceFlagsValueProvider.createCheckFlagsRule();
-
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/RestartDialogLayoutTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/RestartDialogLayoutTest.java
index 6b0c5dd..2cded9d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/RestartDialogLayoutTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/RestartDialogLayoutTest.java
@@ -26,8 +26,6 @@
 import static org.mockito.Mockito.verify;
 
 import android.platform.test.annotations.RequiresFlagsDisabled;
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.DeviceFlagsValueProvider;
 import android.testing.AndroidTestingRunner;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -36,10 +34,8 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.wm.shell.R;
-import com.android.wm.shell.ShellTestCase;
 
 import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
@@ -55,7 +51,7 @@
  */
 @RunWith(AndroidTestingRunner.class)
 @SmallTest
-public class RestartDialogLayoutTest extends ShellTestCase  {
+public class RestartDialogLayoutTest extends CompatUIShellTestCase  {
 
     @Mock private Runnable mDismissCallback;
     @Mock private Consumer<Boolean> mRestartCallback;
@@ -66,10 +62,6 @@
     private View mDialogContainer;
     private CheckBox mDontRepeatCheckBox;
 
-    @Rule
-    public final CheckFlagsRule mCheckFlagsRule =
-            DeviceFlagsValueProvider.createCheckFlagsRule();
-
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/RestartDialogWindowManagerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/RestartDialogWindowManagerTest.java
index cfeef90..ebd0f41 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/RestartDialogWindowManagerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/RestartDialogWindowManagerTest.java
@@ -22,15 +22,12 @@
 import android.app.TaskInfo;
 import android.content.res.Configuration;
 import android.platform.test.annotations.RequiresFlagsDisabled;
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.DeviceFlagsValueProvider;
 import android.testing.AndroidTestingRunner;
 import android.util.Pair;
 
 import androidx.test.filters.SmallTest;
 
 import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.ShellTestCase;
 import com.android.wm.shell.common.DisplayLayout;
 import com.android.wm.shell.common.SyncTransactionQueue;
 import com.android.wm.shell.transition.Transitions;
@@ -38,7 +35,6 @@
 import junit.framework.Assert;
 
 import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
@@ -54,7 +50,7 @@
  */
 @RunWith(AndroidTestingRunner.class)
 @SmallTest
-public class RestartDialogWindowManagerTest extends ShellTestCase {
+public class RestartDialogWindowManagerTest extends CompatUIShellTestCase {
 
     @Mock
     private SyncTransactionQueue mSyncTransactionQueue;
@@ -66,10 +62,6 @@
     private RestartDialogWindowManager mWindowManager;
     private TaskInfo mTaskInfo;
 
-    @Rule
-    public final CheckFlagsRule mCheckFlagsRule =
-            DeviceFlagsValueProvider.createCheckFlagsRule();
-
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsLayoutTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsLayoutTest.java
index e8e68bd..c6532e1 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsLayoutTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsLayoutTest.java
@@ -27,8 +27,6 @@
 import android.app.TaskInfo;
 import android.content.ComponentName;
 import android.platform.test.annotations.RequiresFlagsDisabled;
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.DeviceFlagsValueProvider;
 import android.testing.AndroidTestingRunner;
 import android.util.Pair;
 import android.view.LayoutInflater;
@@ -40,7 +38,6 @@
 
 import com.android.wm.shell.R;
 import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.ShellTestCase;
 import com.android.wm.shell.TestShellExecutor;
 import com.android.wm.shell.common.DisplayLayout;
 import com.android.wm.shell.common.SyncTransactionQueue;
@@ -48,7 +45,6 @@
 import junit.framework.Assert;
 
 import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
@@ -66,7 +62,7 @@
  */
 @RunWith(AndroidTestingRunner.class)
 @SmallTest
-public class UserAspectRatioSettingsLayoutTest extends ShellTestCase {
+public class UserAspectRatioSettingsLayoutTest extends CompatUIShellTestCase {
 
     private static final int TASK_ID = 1;
 
@@ -88,10 +84,6 @@
     private UserAspectRatioSettingsLayout mLayout;
     private TaskInfo mTaskInfo;
 
-    @Rule
-    public final CheckFlagsRule mCheckFlagsRule =
-            DeviceFlagsValueProvider.createCheckFlagsRule();
-
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManagerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManagerTest.java
index 9f86d49..096e900 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManagerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManagerTest.java
@@ -41,8 +41,6 @@
 import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.platform.test.annotations.RequiresFlagsDisabled;
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.DeviceFlagsValueProvider;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper.RunWithLooper;
 import android.util.Pair;
@@ -56,7 +54,6 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.ShellTestCase;
 import com.android.wm.shell.TestShellExecutor;
 import com.android.wm.shell.common.DisplayLayout;
 import com.android.wm.shell.common.SyncTransactionQueue;
@@ -65,7 +62,6 @@
 import junit.framework.Assert;
 
 import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
@@ -87,7 +83,7 @@
 @RunWith(AndroidTestingRunner.class)
 @RunWithLooper
 @SmallTest
-public class UserAspectRatioSettingsWindowManagerTest extends ShellTestCase {
+public class UserAspectRatioSettingsWindowManagerTest extends CompatUIShellTestCase {
 
     private static final int TASK_ID = 1;
 
@@ -112,10 +108,6 @@
 
     private TestShellExecutor mExecutor;
 
-    @Rule
-    public final CheckFlagsRule mCheckFlagsRule =
-            DeviceFlagsValueProvider.createCheckFlagsRule();
-
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/CloseDesktopTaskTransitionHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/CloseDesktopTaskTransitionHandlerTest.kt
index 9b4cc17..db00f41 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/CloseDesktopTaskTransitionHandlerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/CloseDesktopTaskTransitionHandlerTest.kt
@@ -64,7 +64,7 @@
                 context,
                 testExecutor,
                 testExecutor,
-                transactionSupplier
+                transactionSupplier,
             )
     }
 
@@ -81,11 +81,11 @@
                 info =
                     createTransitionInfo(
                         type = WindowManager.TRANSIT_OPEN,
-                        task = createTask(WINDOWING_MODE_FREEFORM)
+                        task = createTask(WINDOWING_MODE_FREEFORM),
                     ),
                 startTransaction = mock(),
                 finishTransaction = mock(),
-                finishCallback = {}
+                finishCallback = {},
             )
 
         assertFalse("Should not animate open transition", animates)
@@ -99,7 +99,7 @@
                 info = createTransitionInfo(task = createTask(WINDOWING_MODE_FULLSCREEN)),
                 startTransaction = mock(),
                 finishTransaction = mock(),
-                finishCallback = {}
+                finishCallback = {},
             )
 
         assertFalse("Should not animate fullscreen task close transition", animates)
@@ -113,11 +113,11 @@
                 info =
                     createTransitionInfo(
                         changeMode = WindowManager.TRANSIT_OPEN,
-                        task = createTask(WINDOWING_MODE_FREEFORM)
+                        task = createTask(WINDOWING_MODE_FREEFORM),
                     ),
                 startTransaction = mock(),
                 finishTransaction = mock(),
-                finishCallback = {}
+                finishCallback = {},
             )
 
         assertFalse("Should not animate opening freeform task close transition", animates)
@@ -131,7 +131,7 @@
                 info = createTransitionInfo(task = createTask(WINDOWING_MODE_FREEFORM)),
                 startTransaction = mock(),
                 finishTransaction = mock(),
-                finishCallback = {}
+                finishCallback = {},
             )
 
         assertTrue("Should animate closing freeform task close transition", animates)
@@ -140,7 +140,7 @@
     private fun createTransitionInfo(
         type: Int = WindowManager.TRANSIT_CLOSE,
         changeMode: Int = WindowManager.TRANSIT_CLOSE,
-        task: RunningTaskInfo
+        task: RunningTaskInfo,
     ): TransitionInfo =
         TransitionInfo(type, 0 /* flags */).apply {
             addChange(
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 41a594a..ecad521 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
@@ -46,6 +46,7 @@
 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.ShellController
 import com.android.wm.shell.sysui.ShellInit
 import com.android.wm.shell.transition.Transitions
 import junit.framework.Assert.assertEquals
@@ -98,6 +99,7 @@
     @Mock lateinit var persistentRepository: DesktopPersistentRepository
     @Mock lateinit var repositoryInitializer: DesktopRepositoryInitializer
     @Mock lateinit var userManager: UserManager
+    @Mock lateinit var shellController: ShellController
 
     private lateinit var mockitoSession: StaticMockitoSession
     private lateinit var handler: DesktopActivityOrientationChangeHandler
@@ -123,19 +125,26 @@
             DesktopUserRepositories(
                 context,
                 shellInit,
+                shellController,
                 persistentRepository,
                 repositoryInitializer,
                 testScope,
-                userManager
+                userManager,
             )
         whenever(shellTaskOrganizer.getRunningTasks(anyInt())).thenAnswer { runningTasks }
         whenever(transitions.startTransition(anyInt(), any(), isNull())).thenAnswer { Binder() }
-        whenever(runBlocking { persistentRepository.readDesktop(any(), any()) }).thenReturn(
-            Desktop.getDefaultInstance()
-        )
+        whenever(runBlocking { persistentRepository.readDesktop(any(), any()) })
+            .thenReturn(Desktop.getDefaultInstance())
 
-        handler = DesktopActivityOrientationChangeHandler(context, shellInit, shellTaskOrganizer,
-            taskStackListener, resizeTransitionHandler, userRepositories)
+        handler =
+            DesktopActivityOrientationChangeHandler(
+                context,
+                shellInit,
+                shellTaskOrganizer,
+                taskStackListener,
+                resizeTransitionHandler,
+                userRepositories,
+            )
 
         shellInit.init()
     }
@@ -158,19 +167,28 @@
         whenever(DesktopModeStatus.canEnterDesktopMode(context)).thenReturn(false)
         clearInvocations(shellInit)
 
-        handler = DesktopActivityOrientationChangeHandler(context, shellInit, shellTaskOrganizer,
-            taskStackListener, resizeTransitionHandler, userRepositories)
+        handler =
+            DesktopActivityOrientationChangeHandler(
+                context,
+                shellInit,
+                shellTaskOrganizer,
+                taskStackListener,
+                resizeTransitionHandler,
+                userRepositories,
+            )
 
-        verify(shellInit, never()).addInitCallback(any(),
-            any<DesktopActivityOrientationChangeHandler>())
+        verify(shellInit, never())
+            .addInitCallback(any(), any<DesktopActivityOrientationChangeHandler>())
     }
 
     @Test
     fun handleActivityOrientationChange_resizeable_doNothing() {
         val task = setUpFreeformTask()
 
-        taskStackListener.onActivityRequestedOrientationChanged(task.taskId,
-            SCREEN_ORIENTATION_LANDSCAPE)
+        taskStackListener.onActivityRequestedOrientationChanged(
+            task.taskId,
+            SCREEN_ORIENTATION_LANDSCAPE,
+        )
 
         verify(resizeTransitionHandler, never()).startTransition(any(), any())
     }
@@ -186,8 +204,10 @@
         userRepositories.current.addTask(DEFAULT_DISPLAY, task.taskId, isVisible = true)
         runningTasks.add(task)
 
-        taskStackListener.onActivityRequestedOrientationChanged(task.taskId,
-            SCREEN_ORIENTATION_LANDSCAPE)
+        taskStackListener.onActivityRequestedOrientationChanged(
+            task.taskId,
+            SCREEN_ORIENTATION_LANDSCAPE,
+        )
 
         verify(resizeTransitionHandler, never()).startTransition(any(), any())
     }
@@ -195,8 +215,11 @@
     @Test
     fun handleActivityOrientationChange_nonResizeablePortrait_requestSameOrientation_doNothing() {
         val task = setUpFreeformTask(isResizeable = false)
-        val newTask = setUpFreeformTask(isResizeable = false,
-            orientation = SCREEN_ORIENTATION_SENSOR_PORTRAIT)
+        val newTask =
+            setUpFreeformTask(
+                isResizeable = false,
+                orientation = SCREEN_ORIENTATION_SENSOR_PORTRAIT,
+            )
 
         handler.handleActivityOrientationChange(task, newTask)
 
@@ -208,8 +231,10 @@
         val task = setUpFreeformTask(isResizeable = false)
         userRepositories.current.updateTask(task.displayId, task.taskId, isVisible = false)
 
-        taskStackListener.onActivityRequestedOrientationChanged(task.taskId,
-            SCREEN_ORIENTATION_LANDSCAPE)
+        taskStackListener.onActivityRequestedOrientationChanged(
+            task.taskId,
+            SCREEN_ORIENTATION_LANDSCAPE,
+        )
 
         verify(resizeTransitionHandler, never()).startTransition(any(), any())
     }
@@ -218,8 +243,8 @@
     fun handleActivityOrientationChange_nonResizeablePortrait_respectLandscapeRequest() {
         val task = setUpFreeformTask(isResizeable = false)
         val oldBounds = task.configuration.windowConfiguration.bounds
-        val newTask = setUpFreeformTask(isResizeable = false,
-            orientation = SCREEN_ORIENTATION_LANDSCAPE)
+        val newTask =
+            setUpFreeformTask(isResizeable = false, orientation = SCREEN_ORIENTATION_LANDSCAPE)
 
         handler.handleActivityOrientationChange(task, newTask)
 
@@ -239,9 +264,12 @@
     @Test
     fun handleActivityOrientationChange_nonResizeableLandscape_respectPortraitRequest() {
         val oldBounds = Rect(0, 0, 500, 200)
-        val task = setUpFreeformTask(
-            isResizeable = false, orientation = SCREEN_ORIENTATION_LANDSCAPE, bounds = oldBounds
-        )
+        val task =
+            setUpFreeformTask(
+                isResizeable = false,
+                orientation = SCREEN_ORIENTATION_LANDSCAPE,
+                bounds = oldBounds,
+            )
         val newTask = setUpFreeformTask(isResizeable = false, bounds = oldBounds)
 
         handler.handleActivityOrientationChange(task, newTask)
@@ -263,7 +291,7 @@
         displayId: Int = DEFAULT_DISPLAY,
         isResizeable: Boolean = true,
         orientation: Int = SCREEN_ORIENTATION_PORTRAIT,
-        bounds: Rect? = Rect(0, 0, 200, 500)
+        bounds: Rect? = Rect(0, 0, 200, 500),
     ): RunningTaskInfo {
         val task = createFreeformTask(displayId, bounds)
         val activityInfo = ActivityInfo()
@@ -288,4 +316,4 @@
 
     private fun findBoundsChange(wct: WindowContainerTransaction, task: RunningTaskInfo): Rect? =
         wct.changes[task.token.asBinder()]?.configuration?.windowConfiguration?.bounds
-}
\ No newline at end of file
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopBackNavigationTransitionHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopBackNavigationTransitionHandlerTest.kt
index 6df8d6f..d14c640 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopBackNavigationTransitionHandlerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopBackNavigationTransitionHandlerTest.kt
@@ -56,11 +56,7 @@
     @Before
     fun setUp() {
         handler =
-            DesktopBackNavigationTransitionHandler(
-                testExecutor,
-                testExecutor,
-                displayController
-            )
+            DesktopBackNavigationTransitionHandler(testExecutor, testExecutor, displayController)
         whenever(displayController.getDisplayContext(any())).thenReturn(mContext)
     }
 
@@ -75,13 +71,13 @@
             handler.startAnimation(
                 transition = mock(),
                 info =
-                createTransitionInfo(
-                    type = WindowManager.TRANSIT_OPEN,
-                    task = createTask(WINDOWING_MODE_FREEFORM)
-                ),
+                    createTransitionInfo(
+                        type = WindowManager.TRANSIT_OPEN,
+                        task = createTask(WINDOWING_MODE_FREEFORM),
+                    ),
                 startTransaction = mock(),
                 finishTransaction = mock(),
-                finishCallback = {}
+                finishCallback = {},
             )
 
         assertFalse("Should not animate open transition", animates)
@@ -95,7 +91,7 @@
                 info = createTransitionInfo(task = createTask(WINDOWING_MODE_FULLSCREEN)),
                 startTransaction = mock(),
                 finishTransaction = mock(),
-                finishCallback = {}
+                finishCallback = {},
             )
 
         assertFalse("Should not animate fullscreen task to back transition", animates)
@@ -107,13 +103,13 @@
             handler.startAnimation(
                 transition = mock(),
                 info =
-                createTransitionInfo(
-                    changeMode = WindowManager.TRANSIT_OPEN,
-                    task = createTask(WINDOWING_MODE_FREEFORM)
-                ),
+                    createTransitionInfo(
+                        changeMode = WindowManager.TRANSIT_OPEN,
+                        task = createTask(WINDOWING_MODE_FREEFORM),
+                    ),
                 startTransaction = mock(),
                 finishTransaction = mock(),
-                finishCallback = {}
+                finishCallback = {},
             )
 
         assertFalse("Should not animate opening freeform task to back transition", animates)
@@ -127,7 +123,7 @@
                 info = createTransitionInfo(task = createTask(WINDOWING_MODE_FREEFORM)),
                 startTransaction = mock(),
                 finishTransaction = mock(),
-                finishCallback = {}
+                finishCallback = {},
             )
 
         assertTrue("Should animate going to back freeform task close transition", animates)
@@ -138,22 +134,24 @@
         val animates =
             handler.startAnimation(
                 transition = mock(),
-                info = createTransitionInfo(
-                    type = TRANSIT_CLOSE,
-                    changeMode = TRANSIT_CLOSE,
-                    task = createTask(WINDOWING_MODE_FREEFORM)
-                ),
+                info =
+                    createTransitionInfo(
+                        type = TRANSIT_CLOSE,
+                        changeMode = TRANSIT_CLOSE,
+                        task = createTask(WINDOWING_MODE_FREEFORM),
+                    ),
                 startTransaction = mock(),
                 finishTransaction = mock(),
-                finishCallback = {}
+                finishCallback = {},
             )
 
         assertTrue("Should animate going to back freeform task close transition", animates)
     }
+
     private fun createTransitionInfo(
         type: Int = WindowManager.TRANSIT_TO_BACK,
         changeMode: Int = WindowManager.TRANSIT_TO_BACK,
-        task: RunningTaskInfo
+        task: RunningTaskInfo,
     ): TransitionInfo =
         TransitionInfo(type, 0 /* flags */).apply {
             addChange(
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandlerTest.kt
index fea8236..6a37174 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandlerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandlerTest.kt
@@ -63,109 +63,115 @@
 @RunWith(AndroidTestingRunner::class)
 class DesktopDisplayEventHandlerTest : ShellTestCase() {
 
-  @Mock lateinit var testExecutor: ShellExecutor
-  @Mock lateinit var transitions: Transitions
-  @Mock lateinit var displayController: DisplayController
-  @Mock lateinit var rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer
-  @Mock private lateinit var mockWindowManager: IWindowManager
+    @Mock lateinit var testExecutor: ShellExecutor
+    @Mock lateinit var transitions: Transitions
+    @Mock lateinit var displayController: DisplayController
+    @Mock lateinit var rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer
+    @Mock private lateinit var mockWindowManager: IWindowManager
 
-  private lateinit var shellInit: ShellInit
-  private lateinit var handler: DesktopDisplayEventHandler
+    private lateinit var shellInit: ShellInit
+    private lateinit var handler: DesktopDisplayEventHandler
 
-  @Before
-  fun setUp() {
-    shellInit = spy(ShellInit(testExecutor))
-    whenever(transitions.startTransition(anyInt(), any(), isNull())).thenAnswer { Binder() }
-    val tda = DisplayAreaInfo(MockToken().token(), DEFAULT_DISPLAY, 0)
-    whenever(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)).thenReturn(tda)
-    handler =
-        DesktopDisplayEventHandler(
-            context,
-            shellInit,
-            transitions,
-            displayController,
-            rootTaskDisplayAreaOrganizer,
-            mockWindowManager,
+    @Before
+    fun setUp() {
+        shellInit = spy(ShellInit(testExecutor))
+        whenever(transitions.startTransition(anyInt(), any(), isNull())).thenAnswer { Binder() }
+        val tda = DisplayAreaInfo(MockToken().token(), DEFAULT_DISPLAY, 0)
+        whenever(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)).thenReturn(tda)
+        handler =
+            DesktopDisplayEventHandler(
+                context,
+                shellInit,
+                transitions,
+                displayController,
+                rootTaskDisplayAreaOrganizer,
+                mockWindowManager,
+            )
+        shellInit.init()
+    }
+
+    private fun testDisplayWindowingModeSwitch(
+        defaultWindowingMode: Int,
+        extendedDisplayEnabled: Boolean,
+        expectTransition: Boolean,
+    ) {
+        val externalDisplayId = 100
+        val captor = ArgumentCaptor.forClass(OnDisplaysChangedListener::class.java)
+        verify(displayController).addDisplayWindowListener(captor.capture())
+        val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!!
+        tda.configuration.windowConfiguration.windowingMode = defaultWindowingMode
+        whenever(mockWindowManager.getWindowingMode(anyInt())).thenAnswer { defaultWindowingMode }
+        val settingsSession =
+            ExtendedDisplaySettingsSession(
+                context.contentResolver,
+                if (extendedDisplayEnabled) 1 else 0,
+            )
+
+        settingsSession.use {
+            // The external display connected.
+            whenever(rootTaskDisplayAreaOrganizer.getDisplayIds())
+                .thenReturn(intArrayOf(DEFAULT_DISPLAY, externalDisplayId))
+            captor.value.onDisplayAdded(externalDisplayId)
+            tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM
+            // The external display disconnected.
+            whenever(rootTaskDisplayAreaOrganizer.getDisplayIds())
+                .thenReturn(intArrayOf(DEFAULT_DISPLAY))
+            captor.value.onDisplayRemoved(externalDisplayId)
+
+            if (expectTransition) {
+                val arg = argumentCaptor<WindowContainerTransaction>()
+                verify(transitions, times(2))
+                    .startTransition(eq(TRANSIT_CHANGE), arg.capture(), isNull())
+                assertThat(arg.firstValue.changes[tda.token.asBinder()]?.windowingMode)
+                    .isEqualTo(WINDOWING_MODE_FREEFORM)
+                assertThat(arg.secondValue.changes[tda.token.asBinder()]?.windowingMode)
+                    .isEqualTo(defaultWindowingMode)
+            } else {
+                verify(transitions, never()).startTransition(eq(TRANSIT_CHANGE), any(), isNull())
+            }
+        }
+    }
+
+    @Test
+    fun displayWindowingModeSwitchOnDisplayConnected_extendedDisplayDisabled() {
+        testDisplayWindowingModeSwitch(
+            defaultWindowingMode = WINDOWING_MODE_FULLSCREEN,
+            extendedDisplayEnabled = false,
+            expectTransition = false,
         )
-    shellInit.init()
-  }
-
-  private fun testDisplayWindowingModeSwitch(
-    defaultWindowingMode: Int,
-    extendedDisplayEnabled: Boolean,
-    expectTransition: Boolean
-  ) {
-    val externalDisplayId = 100
-    val captor = ArgumentCaptor.forClass(OnDisplaysChangedListener::class.java)
-    verify(displayController).addDisplayWindowListener(captor.capture())
-    val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!!
-    tda.configuration.windowConfiguration.windowingMode = defaultWindowingMode
-    whenever(mockWindowManager.getWindowingMode(anyInt())).thenAnswer { defaultWindowingMode }
-    val settingsSession = ExtendedDisplaySettingsSession(
-      context.contentResolver, if (extendedDisplayEnabled) 1 else 0)
-
-    settingsSession.use {
-      // The external display connected.
-      whenever(rootTaskDisplayAreaOrganizer.getDisplayIds())
-        .thenReturn(intArrayOf(DEFAULT_DISPLAY, externalDisplayId))
-      captor.value.onDisplayAdded(externalDisplayId)
-      tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM
-      // The external display disconnected.
-      whenever(rootTaskDisplayAreaOrganizer.getDisplayIds())
-        .thenReturn(intArrayOf(DEFAULT_DISPLAY))
-      captor.value.onDisplayRemoved(externalDisplayId)
-
-      if (expectTransition) {
-        val arg = argumentCaptor<WindowContainerTransaction>()
-        verify(transitions, times(2)).startTransition(eq(TRANSIT_CHANGE), arg.capture(), isNull())
-        assertThat(arg.firstValue.changes[tda.token.asBinder()]?.windowingMode)
-          .isEqualTo(WINDOWING_MODE_FREEFORM)
-        assertThat(arg.secondValue.changes[tda.token.asBinder()]?.windowingMode)
-          .isEqualTo(defaultWindowingMode)
-      } else {
-        verify(transitions, never()).startTransition(eq(TRANSIT_CHANGE), any(), isNull())
-      }
     }
-  }
 
-  @Test
-  fun displayWindowingModeSwitchOnDisplayConnected_extendedDisplayDisabled() {
-    testDisplayWindowingModeSwitch(
-      defaultWindowingMode = WINDOWING_MODE_FULLSCREEN,
-      extendedDisplayEnabled = false,
-      expectTransition = false
-    )
-  }
-
-  @Test
-  fun displayWindowingModeSwitchOnDisplayConnected_fullscreenDisplay() {
-    testDisplayWindowingModeSwitch(
-      defaultWindowingMode = WINDOWING_MODE_FULLSCREEN,
-      extendedDisplayEnabled = true,
-      expectTransition = true
-    )
-  }
-
-  @Test
-  fun displayWindowingModeSwitchOnDisplayConnected_freeformDisplay() {
-    testDisplayWindowingModeSwitch(
-      defaultWindowingMode = WINDOWING_MODE_FREEFORM,
-      extendedDisplayEnabled = true,
-      expectTransition = false
-    )
-  }
-
-  private class ExtendedDisplaySettingsSession(
-    private val contentResolver: ContentResolver,
-      private val overrideValue: Int
-  ) : AutoCloseable {
-    private val settingName = DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS
-    private val initialValue = Settings.Global.getInt(contentResolver, settingName, 0)
-
-    init { Settings.Global.putInt(contentResolver, settingName, overrideValue) }
-
-    override fun close() {
-      Settings.Global.putInt(contentResolver, settingName, initialValue)
+    @Test
+    fun displayWindowingModeSwitchOnDisplayConnected_fullscreenDisplay() {
+        testDisplayWindowingModeSwitch(
+            defaultWindowingMode = WINDOWING_MODE_FULLSCREEN,
+            extendedDisplayEnabled = true,
+            expectTransition = true,
+        )
     }
-  }
-}
\ No newline at end of file
+
+    @Test
+    fun displayWindowingModeSwitchOnDisplayConnected_freeformDisplay() {
+        testDisplayWindowingModeSwitch(
+            defaultWindowingMode = WINDOWING_MODE_FREEFORM,
+            extendedDisplayEnabled = true,
+            expectTransition = false,
+        )
+    }
+
+    private class ExtendedDisplaySettingsSession(
+        private val contentResolver: ContentResolver,
+        private val overrideValue: Int,
+    ) : AutoCloseable {
+        private val settingName = DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS
+        private val initialValue = Settings.Global.getInt(contentResolver, settingName, 0)
+
+        init {
+            Settings.Global.putInt(contentResolver, settingName, overrideValue)
+        }
+
+        override fun close() {
+            Settings.Global.putInt(contentResolver, settingName, initialValue)
+        }
+    }
+}
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 447da87..47d133b 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
@@ -88,9 +88,16 @@
 
     @Before
     fun setUp() {
-        userRepositories = DesktopUserRepositories(
-            context, ShellInit(TestShellExecutor()), mock(), mock(), mock(), mock()
-        )
+        userRepositories =
+            DesktopUserRepositories(
+                context,
+                ShellInit(TestShellExecutor()),
+                mock(),
+                mock(),
+                mock(),
+                mock(),
+                mock(),
+            )
         whenever(mockDisplayController.getDisplayLayout(DEFAULT_DISPLAY))
             .thenReturn(mockDisplayLayout)
         whenever(mockDisplayLayout.getStableBounds(any())).thenAnswer { invocation ->
@@ -98,15 +105,16 @@
         }
         whenever(mockDisplayLayout.width()).thenReturn(DISPLAY_BOUNDS.width())
         whenever(mockDisplayLayout.height()).thenReturn(DISPLAY_BOUNDS.height())
-        controller = DesktopImmersiveController(
-            shellInit = mock(),
-            transitions = mockTransitions,
-            desktopUserRepositories = userRepositories,
-            displayController = mockDisplayController,
-            shellTaskOrganizer = mockShellTaskOrganizer,
-            shellCommandHandler = mock(),
-            transactionSupplier = transactionSupplier,
-        )
+        controller =
+            DesktopImmersiveController(
+                shellInit = mock(),
+                transitions = mockTransitions,
+                desktopUserRepositories = userRepositories,
+                displayController = mockDisplayController,
+                shellTaskOrganizer = mockShellTaskOrganizer,
+                shellCommandHandler = mock(),
+                transactionSupplier = transactionSupplier,
+            )
         desktopRepository = userRepositories.current
     }
 
@@ -119,15 +127,13 @@
         desktopRepository.setTaskInFullImmersiveState(
             displayId = task.displayId,
             taskId = task.taskId,
-            immersive = false
+            immersive = false,
         )
 
         controller.moveTaskToImmersive(task)
         controller.onTransitionReady(
             transition = mockBinder,
-            info = createTransitionInfo(
-                changes = listOf(createChange(task))
-            ),
+            info = createTransitionInfo(changes = listOf(createChange(task))),
             startTransaction = SurfaceControl.Transaction(),
             finishTransaction = SurfaceControl.Transaction(),
         )
@@ -145,16 +151,14 @@
         desktopRepository.setTaskInFullImmersiveState(
             displayId = task.displayId,
             taskId = task.taskId,
-            immersive = false
+            immersive = false,
         )
         assertThat(desktopRepository.removeBoundsBeforeFullImmersive(task.taskId)).isNull()
 
         controller.moveTaskToImmersive(task)
         controller.onTransitionReady(
             transition = mockBinder,
-            info = createTransitionInfo(
-                changes = listOf(createChange(task))
-            ),
+            info = createTransitionInfo(changes = listOf(createChange(task))),
             startTransaction = SurfaceControl.Transaction(),
             finishTransaction = SurfaceControl.Transaction(),
         )
@@ -171,15 +175,13 @@
         desktopRepository.setTaskInFullImmersiveState(
             displayId = task.displayId,
             taskId = task.taskId,
-            immersive = true
+            immersive = true,
         )
 
         controller.moveTaskToNonImmersive(task, USER_INTERACTION)
         controller.onTransitionReady(
             transition = mockBinder,
-            info = createTransitionInfo(
-                changes = listOf(createChange(task))
-            ),
+            info = createTransitionInfo(changes = listOf(createChange(task))),
             startTransaction = SurfaceControl.Transaction(),
             finishTransaction = SurfaceControl.Transaction(),
         )
@@ -197,16 +199,14 @@
         desktopRepository.setTaskInFullImmersiveState(
             displayId = task.displayId,
             taskId = task.taskId,
-            immersive = true
+            immersive = true,
         )
         desktopRepository.saveBoundsBeforeFullImmersive(task.taskId, Rect(100, 100, 600, 600))
 
         controller.moveTaskToNonImmersive(task, USER_INTERACTION)
         controller.onTransitionReady(
             transition = mockBinder,
-            info = createTransitionInfo(
-                changes = listOf(createChange(task))
-            ),
+            info = createTransitionInfo(changes = listOf(createChange(task))),
             startTransaction = SurfaceControl.Transaction(),
             finishTransaction = SurfaceControl.Transaction(),
         )
@@ -220,16 +220,23 @@
         desktopRepository.setTaskInFullImmersiveState(
             displayId = task.displayId,
             taskId = task.taskId,
-            immersive = true
+            immersive = true,
         )
 
         controller.onTransitionReady(
             transition = mock(IBinder::class.java),
-            info = createTransitionInfo(
-                changes = listOf(createChange(task).apply {
-                    setRotation(/* start= */ Surface.ROTATION_0, /* end= */ Surface.ROTATION_90)
-                })
-            ),
+            info =
+                createTransitionInfo(
+                    changes =
+                        listOf(
+                            createChange(task).apply {
+                                setRotation(
+                                    /* start= */ Surface.ROTATION_0,
+                                    /* end= */ Surface.ROTATION_90,
+                                )
+                            }
+                        )
+                ),
             startTransaction = SurfaceControl.Transaction(),
             finishTransaction = SurfaceControl.Transaction(),
         )
@@ -247,8 +254,7 @@
         controller.moveTaskToImmersive(task)
         controller.moveTaskToImmersive(task)
 
-        verify(mockTransitions, times(1))
-            .startTransition(eq(TRANSIT_CHANGE), any(), eq(controller))
+        verify(mockTransitions, times(1)).startTransition(eq(TRANSIT_CHANGE), any(), eq(controller))
     }
 
     @Test
@@ -261,8 +267,7 @@
         controller.moveTaskToNonImmersive(task, USER_INTERACTION)
         controller.moveTaskToNonImmersive(task, USER_INTERACTION)
 
-        verify(mockTransitions, times(1))
-            .startTransition(eq(TRANSIT_CHANGE), any(), eq(controller))
+        verify(mockTransitions, times(1)).startTransition(eq(TRANSIT_CHANGE), any(), eq(controller))
     }
 
     @Test
@@ -275,7 +280,7 @@
         desktopRepository.setTaskInFullImmersiveState(
             displayId = DEFAULT_DISPLAY,
             taskId = task.taskId,
-            immersive = true
+            immersive = true,
         )
 
         controller.exitImmersiveIfApplicable(transition, wct, DEFAULT_DISPLAY, USER_INTERACTION)
@@ -284,7 +289,7 @@
             transition = transition,
             taskId = task.taskId,
             direction = Direction.EXIT,
-            animate = false
+            animate = false,
         )
     }
 
@@ -298,7 +303,7 @@
         desktopRepository.setTaskInFullImmersiveState(
             displayId = DEFAULT_DISPLAY,
             taskId = task.taskId,
-            immersive = false
+            immersive = false,
         )
 
         controller.exitImmersiveIfApplicable(transition, wct, DEFAULT_DISPLAY, USER_INTERACTION)
@@ -307,7 +312,7 @@
             transition = transition,
             taskId = task.taskId,
             direction = Direction.EXIT,
-            animate = false
+            animate = false,
         )
     }
 
@@ -321,7 +326,7 @@
         desktopRepository.setTaskInFullImmersiveState(
             displayId = DEFAULT_DISPLAY,
             taskId = task.taskId,
-            immersive = true
+            immersive = true,
         )
 
         controller.exitImmersiveIfApplicable(transition, wct, DEFAULT_DISPLAY, USER_INTERACTION)
@@ -339,7 +344,7 @@
         desktopRepository.setTaskInFullImmersiveState(
             displayId = DEFAULT_DISPLAY,
             taskId = task.taskId,
-            immersive = false
+            immersive = false,
         )
 
         controller.exitImmersiveIfApplicable(transition, wct, DEFAULT_DISPLAY, USER_INTERACTION)
@@ -357,21 +362,25 @@
         desktopRepository.setTaskInFullImmersiveState(
             displayId = DEFAULT_DISPLAY,
             taskId = task.taskId,
-            immersive = true
+            immersive = true,
         )
 
-        controller.exitImmersiveIfApplicable(
-            wct = wct,
-            displayId = DEFAULT_DISPLAY,
-            excludeTaskId = task.taskId,
-            reason = USER_INTERACTION,
-        ).asExit()?.runOnTransitionStart?.invoke(transition)
+        controller
+            .exitImmersiveIfApplicable(
+                wct = wct,
+                displayId = DEFAULT_DISPLAY,
+                excludeTaskId = task.taskId,
+                reason = USER_INTERACTION,
+            )
+            .asExit()
+            ?.runOnTransitionStart
+            ?.invoke(transition)
 
         assertTransitionNotPending(
             transition = transition,
             taskId = task.taskId,
             animate = false,
-            direction = Direction.EXIT
+            direction = Direction.EXIT,
         )
     }
 
@@ -384,7 +393,7 @@
         desktopRepository.setTaskInFullImmersiveState(
             displayId = DEFAULT_DISPLAY,
             taskId = task.taskId,
-            immersive = true
+            immersive = true,
         )
 
         controller.exitImmersiveIfApplicable(wct = wct, taskInfo = task, reason = USER_INTERACTION)
@@ -401,7 +410,7 @@
         desktopRepository.setTaskInFullImmersiveState(
             displayId = DEFAULT_DISPLAY,
             taskId = task.taskId,
-            immersive = false
+            immersive = false,
         )
 
         controller.exitImmersiveIfApplicable(wct, task, USER_INTERACTION)
@@ -419,17 +428,20 @@
         desktopRepository.setTaskInFullImmersiveState(
             displayId = DEFAULT_DISPLAY,
             taskId = task.taskId,
-            immersive = true
+            immersive = true,
         )
 
-        controller.exitImmersiveIfApplicable(wct, task, USER_INTERACTION)
-            .asExit()?.runOnTransitionStart?.invoke(transition)
+        controller
+            .exitImmersiveIfApplicable(wct, task, USER_INTERACTION)
+            .asExit()
+            ?.runOnTransitionStart
+            ?.invoke(transition)
 
         assertTransitionPending(
             transition = transition,
             taskId = task.taskId,
             direction = Direction.EXIT,
-            animate = false
+            animate = false,
         )
     }
 
@@ -442,7 +454,7 @@
         desktopRepository.setTaskInFullImmersiveState(
             displayId = task.displayId,
             taskId = task.taskId,
-            immersive = false
+            immersive = false,
         )
 
         val result = controller.exitImmersiveIfApplicable(wct, task, USER_INTERACTION)
@@ -459,11 +471,16 @@
         desktopRepository.setTaskInFullImmersiveState(
             displayId = task.displayId,
             taskId = task.taskId,
-            immersive = false
+            immersive = false,
         )
 
-        val result = controller.exitImmersiveIfApplicable(
-            wct, task.displayId, excludeTaskId = null, USER_INTERACTION)
+        val result =
+            controller.exitImmersiveIfApplicable(
+                wct,
+                task.displayId,
+                excludeTaskId = null,
+                USER_INTERACTION,
+            )
 
         assertThat(result).isEqualTo(DesktopImmersiveController.ExitResult.NoExit)
     }
@@ -478,15 +495,13 @@
         desktopRepository.setTaskInFullImmersiveState(
             displayId = DEFAULT_DISPLAY,
             taskId = task.taskId,
-            immersive = true
+            immersive = true,
         )
         controller.exitImmersiveIfApplicable(transition, wct, DEFAULT_DISPLAY, USER_INTERACTION)
 
         controller.onTransitionReady(
             transition = transition,
-            info = createTransitionInfo(
-                changes = listOf(createChange(task))
-            ),
+            info = createTransitionInfo(changes = listOf(createChange(task))),
             startTransaction = SurfaceControl.Transaction(),
             finishTransaction = SurfaceControl.Transaction(),
         )
@@ -496,7 +511,7 @@
             transition = transition,
             taskId = task.taskId,
             direction = Direction.EXIT,
-            animate = false
+            animate = false,
         )
     }
 
@@ -511,15 +526,13 @@
         desktopRepository.setTaskInFullImmersiveState(
             displayId = DEFAULT_DISPLAY,
             taskId = task.taskId,
-            immersive = true
+            immersive = true,
         )
         controller.exitImmersiveIfApplicable(transition, wct, DEFAULT_DISPLAY, USER_INTERACTION)
 
         controller.onTransitionReady(
             transition = transition,
-            info = createTransitionInfo(
-                changes = listOf(createChange(task))
-            ),
+            info = createTransitionInfo(changes = listOf(createChange(task))),
             startTransaction = SurfaceControl.Transaction(),
             finishTransaction = SurfaceControl.Transaction(),
         )
@@ -530,13 +543,13 @@
             transition = transition,
             taskId = task.taskId,
             animate = false,
-            direction = Direction.EXIT
+            direction = Direction.EXIT,
         )
         assertTransitionNotPending(
             transition = mergedToTransition,
             taskId = task.taskId,
             animate = false,
-            direction = Direction.EXIT
+            direction = Direction.EXIT,
         )
     }
 
@@ -550,15 +563,13 @@
         desktopRepository.setTaskInFullImmersiveState(
             displayId = DEFAULT_DISPLAY,
             taskId = task.taskId,
-            immersive = true
+            immersive = true,
         )
         controller.exitImmersiveIfApplicable(transition, wct, DEFAULT_DISPLAY, USER_INTERACTION)
 
         controller.onTransitionReady(
             transition = transition,
-            info = createTransitionInfo(
-                changes = listOf(createChange(task))
-            ),
+            info = createTransitionInfo(changes = listOf(createChange(task))),
             startTransaction = SurfaceControl.Transaction(),
             finishTransaction = SurfaceControl.Transaction(),
         )
@@ -569,7 +580,7 @@
     @Test
     @EnableFlags(
         Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP,
-        Flags.FLAG_ENABLE_RESTORE_TO_PREVIOUS_SIZE_FROM_DESKTOP_IMMERSIVE
+        Flags.FLAG_ENABLE_RESTORE_TO_PREVIOUS_SIZE_FROM_DESKTOP_IMMERSIVE,
     )
     fun onTransitionReady_pendingExit_removesBoundsBeforeImmersive() {
         val task = createFreeformTask()
@@ -579,16 +590,14 @@
         desktopRepository.setTaskInFullImmersiveState(
             displayId = DEFAULT_DISPLAY,
             taskId = task.taskId,
-            immersive = true
+            immersive = true,
         )
         desktopRepository.saveBoundsBeforeFullImmersive(task.taskId, Rect(100, 100, 600, 600))
         controller.exitImmersiveIfApplicable(transition, wct, DEFAULT_DISPLAY, USER_INTERACTION)
 
         controller.onTransitionReady(
             transition = transition,
-            info = createTransitionInfo(
-                changes = listOf(createChange(task))
-            ),
+            info = createTransitionInfo(changes = listOf(createChange(task))),
             startTransaction = SurfaceControl.Transaction(),
             finishTransaction = SurfaceControl.Transaction(),
         )
@@ -606,20 +615,21 @@
         desktopRepository.setTaskInFullImmersiveState(
             displayId = DEFAULT_DISPLAY,
             taskId = task.taskId,
-            immersive = true
+            immersive = true,
         )
 
         controller.exitImmersiveIfApplicable(wct = wct, taskInfo = task, reason = USER_INTERACTION)
 
         assertThat(
-            wct.hasBoundsChange(task.token, calculateMaximizeBounds(mockDisplayLayout, task))
-        ).isTrue()
+                wct.hasBoundsChange(task.token, calculateMaximizeBounds(mockDisplayLayout, task))
+            )
+            .isTrue()
     }
 
     @Test
     @EnableFlags(
         Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP,
-        Flags.FLAG_ENABLE_RESTORE_TO_PREVIOUS_SIZE_FROM_DESKTOP_IMMERSIVE
+        Flags.FLAG_ENABLE_RESTORE_TO_PREVIOUS_SIZE_FROM_DESKTOP_IMMERSIVE,
     )
     fun exitImmersiveIfApplicable_preImmersiveBoundsSaved_changesBoundsToPreImmersiveBounds() {
         val task = createFreeformTask()
@@ -628,23 +638,21 @@
         desktopRepository.setTaskInFullImmersiveState(
             displayId = DEFAULT_DISPLAY,
             taskId = task.taskId,
-            immersive = true
+            immersive = true,
         )
         val preImmersiveBounds = Rect(100, 100, 500, 500)
         desktopRepository.saveBoundsBeforeFullImmersive(task.taskId, preImmersiveBounds)
 
         controller.exitImmersiveIfApplicable(wct = wct, taskInfo = task, reason = USER_INTERACTION)
 
-        assertThat(
-            wct.hasBoundsChange(task.token, preImmersiveBounds)
-        ).isTrue()
+        assertThat(wct.hasBoundsChange(task.token, preImmersiveBounds)).isTrue()
     }
 
     @Test
     @EnableFlags(
         Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP,
         Flags.FLAG_ENABLE_RESTORE_TO_PREVIOUS_SIZE_FROM_DESKTOP_IMMERSIVE,
-        Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS
+        Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS,
     )
     fun exitImmersiveIfApplicable_preImmersiveBoundsNotSaved_changesBoundsToInitialBounds() {
         val task = createFreeformTask()
@@ -653,14 +661,13 @@
         desktopRepository.setTaskInFullImmersiveState(
             displayId = DEFAULT_DISPLAY,
             taskId = task.taskId,
-            immersive = true
+            immersive = true,
         )
 
         controller.exitImmersiveIfApplicable(wct = wct, taskInfo = task, reason = USER_INTERACTION)
 
-        assertThat(
-            wct.hasBoundsChange(task.token, calculateInitialBounds(mockDisplayLayout, task))
-        ).isTrue()
+        assertThat(wct.hasBoundsChange(task.token, calculateInitialBounds(mockDisplayLayout, task)))
+            .isTrue()
     }
 
     @Test
@@ -672,10 +679,13 @@
         desktopRepository.setTaskInFullImmersiveState(
             displayId = task.displayId,
             taskId = task.taskId,
-            immersive = true
+            immersive = true,
         )
-        controller.exitImmersiveIfApplicable(wct, task, USER_INTERACTION)
-            .asExit()?.runOnTransitionStart?.invoke(Binder())
+        controller
+            .exitImmersiveIfApplicable(wct, task, USER_INTERACTION)
+            .asExit()
+            ?.runOnTransitionStart
+            ?.invoke(Binder())
 
         controller.moveTaskToNonImmersive(task, USER_INTERACTION)
 
@@ -693,7 +703,7 @@
         desktopRepository.setTaskInFullImmersiveState(
             displayId = DEFAULT_DISPLAY,
             taskId = task.taskId,
-            immersive = true
+            immersive = true,
         )
 
         controller.exitImmersiveIfApplicable(transition, wct, DEFAULT_DISPLAY, USER_INTERACTION)
@@ -711,25 +721,23 @@
         desktopRepository.setTaskInFullImmersiveState(
             displayId = task.displayId,
             taskId = task.taskId,
-            immersive = true
+            immersive = true,
         )
 
         controller.moveTaskToNonImmersive(task, USER_INTERACTION)
 
         controller.animateResizeChange(
-            change = TransitionInfo.Change(task.token, SurfaceControl()).apply {
-                taskInfo = task
-            },
+            change = TransitionInfo.Change(task.token, SurfaceControl()).apply { taskInfo = task },
             startTransaction = StubTransaction(),
             finishTransaction = StubTransaction(),
-            finishCallback = { }
+            finishCallback = {},
         )
         animatorTestRule.advanceTimeBy(DesktopImmersiveController.FULL_IMMERSIVE_ANIM_DURATION_MS)
 
         assertTransitionPending(
             transition = mockBinder,
             taskId = task.taskId,
-            direction = Direction.EXIT
+            direction = Direction.EXIT,
         )
     }
 
@@ -743,7 +751,7 @@
         desktopRepository.setTaskInFullImmersiveState(
             displayId = task.displayId,
             taskId = task.taskId,
-            immersive = false
+            immersive = false,
         )
 
         controller.moveTaskToImmersive(task)
@@ -753,13 +761,13 @@
             info = createTransitionInfo(changes = emptyList()),
             startTransaction = StubTransaction(),
             finishTransaction = StubTransaction(),
-            finishCallback = {}
+            finishCallback = {},
         )
 
         assertTransitionNotPending(
             transition = mockBinder,
             taskId = task.taskId,
-            direction = Direction.ENTER
+            direction = Direction.ENTER,
         )
     }
 
@@ -768,15 +776,18 @@
         taskId: Int,
         direction: Direction,
         animate: Boolean = true,
-        displayId: Int = DEFAULT_DISPLAY
+        displayId: Int = DEFAULT_DISPLAY,
     ) {
-        assertThat(controller.pendingImmersiveTransitions.any { pendingTransition ->
-            pendingTransition.transition == transition
-                    && pendingTransition.displayId == displayId
-                    && pendingTransition.taskId == taskId
-                    && pendingTransition.animate == animate
-                    && pendingTransition.direction == direction
-        }).isTrue()
+        assertThat(
+                controller.pendingImmersiveTransitions.any { pendingTransition ->
+                    pendingTransition.transition == transition &&
+                        pendingTransition.displayId == displayId &&
+                        pendingTransition.taskId == taskId &&
+                        pendingTransition.animate == animate &&
+                        pendingTransition.direction == direction
+                }
+            )
+            .isTrue()
     }
 
     private fun assertTransitionNotPending(
@@ -784,43 +795,44 @@
         taskId: Int,
         direction: Direction,
         animate: Boolean = true,
-        displayId: Int = DEFAULT_DISPLAY
+        displayId: Int = DEFAULT_DISPLAY,
     ) {
-        assertThat(controller.pendingImmersiveTransitions.any { pendingTransition ->
-            pendingTransition.transition == transition
-                    && pendingTransition.displayId == displayId
-                    && pendingTransition.taskId == taskId
-                    && pendingTransition.direction == direction
-        }).isFalse()
+        assertThat(
+                controller.pendingImmersiveTransitions.any { pendingTransition ->
+                    pendingTransition.transition == transition &&
+                        pendingTransition.displayId == displayId &&
+                        pendingTransition.taskId == taskId &&
+                        pendingTransition.direction == direction
+                }
+            )
+            .isFalse()
     }
 
     private fun createTransitionInfo(
         @TransitionType type: Int = TRANSIT_CHANGE,
         @TransitionFlags flags: Int = 0,
-        changes: List<TransitionInfo.Change> = emptyList()
-    ): TransitionInfo = TransitionInfo(type, flags).apply {
-        changes.forEach { change -> addChange(change) }
-    }
+        changes: List<TransitionInfo.Change> = emptyList(),
+    ): TransitionInfo =
+        TransitionInfo(type, flags).apply { changes.forEach { change -> addChange(change) } }
 
     private fun createChange(task: RunningTaskInfo): TransitionInfo.Change =
-        TransitionInfo.Change(task.token, SurfaceControl()).apply {
-            taskInfo = task
-        }
+        TransitionInfo.Change(task.token, SurfaceControl()).apply { taskInfo = task }
 
     private fun WindowContainerTransaction.hasBoundsChange(token: WindowContainerToken): Boolean =
         this.changes.any { change ->
-            change.key == token.asBinder()
-                    && (change.value.windowSetMask and WINDOW_CONFIG_BOUNDS) != 0
+            change.key == token.asBinder() &&
+                (change.value.windowSetMask and WINDOW_CONFIG_BOUNDS) != 0
         }
 
     private fun WindowContainerTransaction.hasBoundsChange(
         token: WindowContainerToken,
         bounds: Rect,
-    ): Boolean = this.changes.any { change ->
-        change.key == token.asBinder()
-                && (change.value.windowSetMask and WINDOW_CONFIG_BOUNDS) != 0
-                && change.value.configuration.windowConfiguration.bounds == bounds
-    }
+    ): Boolean =
+        this.changes.any { change ->
+            change.key == token.asBinder() &&
+                (change.value.windowSetMask and WINDOW_CONFIG_BOUNDS) != 0 &&
+                change.value.configuration.windowConfiguration.bounds == bounds
+        }
 
     companion object {
         private val STABLE_BOUNDS = Rect(0, 100, 2000, 1900)
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 49a7e29..3cf84d9 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
@@ -85,34 +85,22 @@
 
     @JvmField @Rule val setFlagsRule = SetFlagsRule()
 
-    @Mock
-    lateinit var transitions: Transitions
-    @Mock
-    lateinit var userRepositories: DesktopUserRepositories
-    @Mock
-    lateinit var freeformTaskTransitionHandler: FreeformTaskTransitionHandler
-    @Mock
-    lateinit var closeDesktopTaskTransitionHandler: CloseDesktopTaskTransitionHandler
+    @Mock lateinit var transitions: Transitions
+    @Mock lateinit var userRepositories: DesktopUserRepositories
+    @Mock lateinit var freeformTaskTransitionHandler: FreeformTaskTransitionHandler
+    @Mock lateinit var closeDesktopTaskTransitionHandler: CloseDesktopTaskTransitionHandler
     @Mock
     lateinit var desktopBackNavigationTransitionHandler: DesktopBackNavigationTransitionHandler
-    @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
-    @Mock
-    private lateinit var desktopRepository: DesktopRepository
+    @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
+    @Mock private lateinit var desktopRepository: DesktopRepository
 
     private lateinit var mixedHandler: DesktopMixedTransitionHandler
 
-
     @Before
     fun setUp() {
         whenever(userRepositories.current).thenReturn(desktopRepository)
@@ -157,11 +145,11 @@
     @Test
     @DisableFlags(
         Flags.FLAG_ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS,
-        Flags.FLAG_ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS_BUGFIX)
+        Flags.FLAG_ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS_BUGFIX,
+    )
     fun startRemoveTransition_callsFreeformTaskTransitionHandler() {
         val wct = WindowContainerTransaction()
-        whenever(freeformTaskTransitionHandler.startRemoveTransition(wct))
-            .thenReturn(mock())
+        whenever(freeformTaskTransitionHandler.startRemoveTransition(wct)).thenReturn(mock())
 
         mixedHandler.startRemoveTransition(wct)
 
@@ -171,7 +159,8 @@
     @Test
     @EnableFlags(
         Flags.FLAG_ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS,
-        Flags.FLAG_ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS_BUGFIX)
+        Flags.FLAG_ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS_BUGFIX,
+    )
     fun startRemoveTransition_startsCloseTransition() {
         val wct = WindowContainerTransaction()
         whenever(transitions.startTransition(WindowManager.TRANSIT_CLOSE, wct, mixedHandler))
@@ -193,18 +182,19 @@
         val transitionInfo =
             createCloseTransitionInfo(
                 changeMode = TRANSIT_OPEN,
-                task = createTask(WINDOWING_MODE_FREEFORM)
+                task = createTask(WINDOWING_MODE_FREEFORM),
             )
         whenever(freeformTaskTransitionHandler.startAnimation(any(), any(), any(), any(), any()))
             .thenReturn(true)
 
-        val started = mixedHandler.startAnimation(
-            transition = transition,
-            info = transitionInfo,
-            startTransaction = mock(),
-            finishTransaction = mock(),
-            finishCallback = {}
-        )
+        val started =
+            mixedHandler.startAnimation(
+                transition = transition,
+                info = transitionInfo,
+                startTransaction = mock(),
+                finishTransaction = mock(),
+                finishCallback = {},
+            )
 
         assertFalse("Should not start animation without closing desktop task", started)
     }
@@ -212,7 +202,8 @@
     @Test
     @EnableFlags(
         Flags.FLAG_ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS,
-        Flags.FLAG_ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS_BUGFIX)
+        Flags.FLAG_ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS_BUGFIX,
+    )
     fun startAnimation_withClosingDesktopTask_callsCloseTaskHandler() {
         val wct = WindowContainerTransaction()
         val transition = mock<IBinder>()
@@ -225,13 +216,14 @@
             .thenReturn(transition)
         mixedHandler.startRemoveTransition(wct)
 
-        val started = mixedHandler.startAnimation(
-            transition = transition,
-            info = transitionInfo,
-            startTransaction = mock(),
-            finishTransaction = mock(),
-            finishCallback = {}
-        )
+        val started =
+            mixedHandler.startAnimation(
+                transition = transition,
+                info = transitionInfo,
+                startTransaction = mock(),
+                finishTransaction = mock(),
+                finishCallback = {},
+            )
 
         assertTrue("Should delegate animation to close transition handler", started)
         verify(closeDesktopTaskTransitionHandler)
@@ -241,12 +233,16 @@
     @Test
     @EnableFlags(
         Flags.FLAG_ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS,
-        Flags.FLAG_ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS_BUGFIX)
+        Flags.FLAG_ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS_BUGFIX,
+    )
     fun startAnimation_withClosingLastDesktopTask_dispatchesTransition() {
         val wct = WindowContainerTransaction()
         val transition = mock<IBinder>()
-        val transitionInfo = createCloseTransitionInfo(
-            task = createTask(WINDOWING_MODE_FREEFORM), withWallpaper = true)
+        val transitionInfo =
+            createCloseTransitionInfo(
+                task = createTask(WINDOWING_MODE_FREEFORM),
+                withWallpaper = true,
+            )
         whenever(transitions.dispatchTransition(any(), any(), any(), any(), any(), any()))
             .thenReturn(mock())
         whenever(transitions.startTransition(WindowManager.TRANSIT_CLOSE, wct, mixedHandler))
@@ -258,7 +254,7 @@
             info = transitionInfo,
             startTransaction = mock(),
             finishTransaction = mock(),
-            finishCallback = {}
+            finishCallback = {},
         )
 
         verify(transitions)
@@ -268,14 +264,14 @@
                 any(),
                 any(),
                 any(),
-                eq(mixedHandler)
+                eq(mixedHandler),
             )
         verify(interactionJankMonitor)
             .begin(
                 closingTaskLeash,
                 context,
                 mockHandler,
-                CUJ_DESKTOP_MODE_EXIT_MODE_ON_LAST_WINDOW_CLOSE
+                CUJ_DESKTOP_MODE_EXIT_MODE_ON_LAST_WINDOW_CLOSE,
             )
     }
 
@@ -283,7 +279,8 @@
     @DisableFlags(
         Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP,
         Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS,
-        Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS_BUGFIX)
+        Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS_BUGFIX,
+    )
     fun startLaunchTransition_immersiveAndAppLaunchFlagsDisabled_doesNotUseMixedHandler() {
         val wct = WindowContainerTransaction()
         val task = createTask(WINDOWING_MODE_FREEFORM)
@@ -294,7 +291,7 @@
             transitionType = TRANSIT_OPEN,
             wct = wct,
             taskId = task.taskId,
-            exitingImmersiveTask = null
+            exitingImmersiveTask = null,
         )
 
         verify(transitions).startTransition(TRANSIT_OPEN, wct, /* handler= */ null)
@@ -312,7 +309,7 @@
             transitionType = TRANSIT_OPEN,
             wct = wct,
             taskId = task.taskId,
-            exitingImmersiveTask = null
+            exitingImmersiveTask = null,
         )
 
         verify(transitions).startTransition(TRANSIT_OPEN, wct, mixedHandler)
@@ -321,7 +318,8 @@
     @Test
     @EnableFlags(
         Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS,
-        Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS_BUGFIX)
+        Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS_BUGFIX,
+    )
     fun startLaunchTransition_desktopAppLaunchEnabled_usesMixedHandler() {
         val wct = WindowContainerTransaction()
         val task = createTask(WINDOWING_MODE_FREEFORM)
@@ -332,7 +330,7 @@
             transitionType = TRANSIT_OPEN,
             wct = wct,
             taskId = task.taskId,
-            exitingImmersiveTask = null
+            exitingImmersiveTask = null,
         )
 
         verify(transitions).startTransition(TRANSIT_OPEN, wct, mixedHandler)
@@ -357,24 +355,22 @@
         val otherChange = createChange(createTask(WINDOWING_MODE_FREEFORM))
         mixedHandler.startAnimation(
             transition,
-            createCloseTransitionInfo(
-                TRANSIT_OPEN,
-                listOf(launchTaskChange, otherChange)
-            ),
+            createCloseTransitionInfo(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),
-        )
+        verify(transitions)
+            .dispatchTransition(
+                eq(transition),
+                argThat { info ->
+                    info.changes.contains(launchTaskChange) && info.changes.contains(otherChange)
+                },
+                any(),
+                any(),
+                any(),
+                eq(mixedHandler),
+            )
     }
 
     @Test
@@ -397,32 +393,32 @@
         val immersiveChange = createChange(immersiveTask)
         mixedHandler.startAnimation(
             transition,
-            createCloseTransitionInfo(
-                TRANSIT_OPEN,
-                listOf(launchTaskChange, immersiveChange)
-            ),
+            createCloseTransitionInfo(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),
-        )
+        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,
-        Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS_BUGFIX)
+        Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS_BUGFIX,
+    )
     fun startAndAnimateLaunchTransition_noMinimizeChange_doesNotReparentMinimizeChange() {
         val wct = WindowContainerTransaction()
         val launchingTask = createTask(WINDOWING_MODE_FREEFORM)
@@ -439,22 +435,19 @@
         )
         mixedHandler.startAnimation(
             transition,
-            createCloseTransitionInfo(
-                TRANSIT_OPEN,
-                listOf(launchTaskChange)
-            ),
+            createCloseTransitionInfo(TRANSIT_OPEN, listOf(launchTaskChange)),
             SurfaceControl.Transaction(),
             SurfaceControl.Transaction(),
-        ) { }
+        ) {}
 
-        verify(rootTaskDisplayAreaOrganizer, times(0))
-            .reparentToDisplayArea(anyInt(), any(), any())
+        verify(rootTaskDisplayAreaOrganizer, times(0)).reparentToDisplayArea(anyInt(), any(), any())
     }
 
     @Test
     @EnableFlags(
         Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS,
-        Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS_BUGFIX)
+        Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS_BUGFIX,
+    )
     fun startAndAnimateLaunchTransition_withMinimizeChange_reparentsMinimizeChange() {
         val wct = WindowContainerTransaction()
         val launchingTask = createTask(WINDOWING_MODE_FREEFORM)
@@ -473,22 +466,20 @@
         )
         mixedHandler.startAnimation(
             transition,
-            createCloseTransitionInfo(
-                TRANSIT_OPEN,
-                listOf(launchTaskChange, minimizeChange)
-            ),
+            createCloseTransitionInfo(TRANSIT_OPEN, listOf(launchTaskChange, minimizeChange)),
             SurfaceControl.Transaction(),
             SurfaceControl.Transaction(),
-        ) { }
+        ) {}
 
-        verify(rootTaskDisplayAreaOrganizer).reparentToDisplayArea(
-            anyInt(), eq(minimizeChange.leash), any())
+        verify(rootTaskDisplayAreaOrganizer)
+            .reparentToDisplayArea(anyInt(), eq(minimizeChange.leash), any())
     }
 
     @Test
     @EnableFlags(
         Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS,
-        Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS_BUGFIX)
+        Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS_BUGFIX,
+    )
     fun startAnimation_pendingTransition_noLaunchChange_returnsFalse() {
         val wct = WindowContainerTransaction()
         val launchingTask = createTask(WINDOWING_MODE_FREEFORM)
@@ -505,15 +496,13 @@
             )
         )
 
-        val started = mixedHandler.startAnimation(
-            transition,
-            createCloseTransitionInfo(
-                TRANSIT_OPEN,
-                listOf(nonLaunchTaskChange)
-            ),
-            SurfaceControl.Transaction(),
-            SurfaceControl.Transaction(),
-        ) { }
+        val started =
+            mixedHandler.startAnimation(
+                transition,
+                createCloseTransitionInfo(TRANSIT_OPEN, listOf(nonLaunchTaskChange)),
+                SurfaceControl.Transaction(),
+                SurfaceControl.Transaction(),
+            ) {}
 
         assertFalse("Should not start animation without launching desktop task", started)
     }
@@ -529,21 +518,18 @@
         whenever(transitions.dispatchTransition(eq(transition), any(), any(), any(), any(), any()))
             .thenReturn(mock())
 
-        mixedHandler.startLaunchTransition(
-            transitionType = TRANSIT_OPEN,
-            wct = wct,
-            taskId = null,
-        )
+        mixedHandler.startLaunchTransition(transitionType = TRANSIT_OPEN, wct = wct, taskId = null)
 
-        val started = mixedHandler.startAnimation(
-            transition,
-            createCloseTransitionInfo(
-                TRANSIT_OPEN,
-                listOf(createChange(task, mode = TRANSIT_OPEN))
-            ),
-            StubTransaction(),
-            StubTransaction(),
-        ) { }
+        val started =
+            mixedHandler.startAnimation(
+                transition,
+                createCloseTransitionInfo(
+                    TRANSIT_OPEN,
+                    listOf(createChange(task, mode = TRANSIT_OPEN)),
+                ),
+                StubTransaction(),
+                StubTransaction(),
+            ) {}
 
         assertThat(started).isEqualTo(true)
     }
@@ -569,15 +555,13 @@
 
         val immersiveChange = createChange(immersiveTask, mode = TRANSIT_CHANGE)
         val openingChange = createChange(openingTask, mode = TRANSIT_OPEN)
-        val started = mixedHandler.startAnimation(
-            transition,
-            createCloseTransitionInfo(
-                TRANSIT_OPEN,
-                listOf(immersiveChange, openingChange)
-            ),
-            StubTransaction(),
-            StubTransaction(),
-        ) { }
+        val started =
+            mixedHandler.startAnimation(
+                transition,
+                createCloseTransitionInfo(TRANSIT_OPEN, listOf(immersiveChange, openingChange)),
+                StubTransaction(),
+                StubTransaction(),
+            ) {}
 
         assertThat(started).isEqualTo(true)
         verify(desktopImmersiveController)
@@ -587,7 +571,8 @@
     @Test
     @EnableFlags(
         Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS,
-        Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS_BUGFIX)
+        Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS_BUGFIX,
+    )
     fun addPendingAndAnimateLaunchTransition_noMinimizeChange_doesNotReparentMinimizeChange() {
         val wct = WindowContainerTransaction()
         val launchingTask = createTask(WINDOWING_MODE_FREEFORM)
@@ -606,22 +591,19 @@
         )
         mixedHandler.startAnimation(
             transition,
-            createCloseTransitionInfo(
-                TRANSIT_OPEN,
-                listOf(launchTaskChange)
-            ),
+            createCloseTransitionInfo(TRANSIT_OPEN, listOf(launchTaskChange)),
             SurfaceControl.Transaction(),
             SurfaceControl.Transaction(),
-        ) { }
+        ) {}
 
-        verify(rootTaskDisplayAreaOrganizer, times(0))
-            .reparentToDisplayArea(anyInt(), any(), any())
+        verify(rootTaskDisplayAreaOrganizer, times(0)).reparentToDisplayArea(anyInt(), any(), any())
     }
 
     @Test
     @EnableFlags(
         Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS,
-        Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS_BUGFIX)
+        Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS_BUGFIX,
+    )
     fun addPendingAndAnimateLaunchTransition_withMinimizeChange_reparentsMinimizeChange() {
         val wct = WindowContainerTransaction()
         val launchingTask = createTask(WINDOWING_MODE_FREEFORM)
@@ -642,16 +624,13 @@
         )
         mixedHandler.startAnimation(
             transition,
-            createCloseTransitionInfo(
-                TRANSIT_OPEN,
-                listOf(launchTaskChange, minimizeChange)
-            ),
+            createCloseTransitionInfo(TRANSIT_OPEN, listOf(launchTaskChange, minimizeChange)),
             SurfaceControl.Transaction(),
             SurfaceControl.Transaction(),
-        ) { }
+        ) {}
 
-        verify(rootTaskDisplayAreaOrganizer).reparentToDisplayArea(
-            anyInt(), eq(minimizeChange.leash), any())
+        verify(rootTaskDisplayAreaOrganizer)
+            .reparentToDisplayArea(anyInt(), eq(minimizeChange.leash), any())
     }
 
     @Test
@@ -672,13 +651,10 @@
         val launchTaskChange = createChange(launchingTask)
         mixedHandler.startAnimation(
             transition,
-            createCloseTransitionInfo(
-                TRANSIT_OPEN,
-                listOf(launchTaskChange)
-            ),
+            createCloseTransitionInfo(TRANSIT_OPEN, listOf(launchTaskChange)),
             SurfaceControl.Transaction(),
             SurfaceControl.Transaction(),
-        ) { }
+        ) {}
 
         assertThat(mixedHandler.pendingMixedTransitions).isEmpty()
     }
@@ -701,7 +677,7 @@
         mixedHandler.onTransitionConsumed(
             transition = transition,
             aborted = true,
-            finishTransaction = SurfaceControl.Transaction()
+            finishTransaction = SurfaceControl.Transaction(),
         )
 
         assertThat(mixedHandler.pendingMixedTransitions).isEmpty()
@@ -714,8 +690,14 @@
         val transition = Binder()
         whenever(desktopRepository.getExpandedTaskCount(any())).thenReturn(2)
         whenever(
-            desktopBackNavigationTransitionHandler.startAnimation(any(), any(), any(), any(), any())
-        )
+                desktopBackNavigationTransitionHandler.startAnimation(
+                    any(),
+                    any(),
+                    any(),
+                    any(),
+                    any(),
+                )
+            )
             .thenReturn(true)
         mixedHandler.addPendingMixedTransition(
             PendingMixedTransition.Minimize(
@@ -726,24 +708,24 @@
         )
 
         val minimizingTaskChange = createChange(minimizingTask)
-        val started = mixedHandler.startAnimation(
-            transition = transition,
-            info =
-                createCloseTransitionInfo(
-                TRANSIT_TO_BACK,
-                listOf(minimizingTaskChange)
-            ),
-            startTransaction = mock(),
-            finishTransaction = mock(),
-            finishCallback = {}
-        )
+        val started =
+            mixedHandler.startAnimation(
+                transition = transition,
+                info = createCloseTransitionInfo(TRANSIT_TO_BACK, listOf(minimizingTaskChange)),
+                startTransaction = mock(),
+                finishTransaction = mock(),
+                finishCallback = {},
+            )
 
         assertTrue("Should delegate animation to back navigation transition handler", started)
         verify(desktopBackNavigationTransitionHandler)
             .startAnimation(
                 eq(transition),
                 argThat { info -> info.changes.contains(minimizingTaskChange) },
-                any(), any(), any())
+                any(),
+                any(),
+                any(),
+            )
     }
 
     @Test
@@ -753,8 +735,14 @@
         val transition = Binder()
         whenever(desktopRepository.getExpandedTaskCount(any())).thenReturn(2)
         whenever(
-            desktopBackNavigationTransitionHandler.startAnimation(any(), any(), any(), any(), any())
-        )
+                desktopBackNavigationTransitionHandler.startAnimation(
+                    any(),
+                    any(),
+                    any(),
+                    any(),
+                    any(),
+                )
+            )
             .thenReturn(true)
         mixedHandler.addPendingMixedTransition(
             PendingMixedTransition.Minimize(
@@ -767,14 +755,10 @@
         val minimizingTaskChange = createChange(minimizingTask)
         mixedHandler.startAnimation(
             transition = transition,
-            info =
-            createCloseTransitionInfo(
-                TRANSIT_TO_BACK,
-                listOf(minimizingTaskChange)
-            ),
+            info = createCloseTransitionInfo(TRANSIT_TO_BACK, listOf(minimizingTaskChange)),
             startTransaction = mock(),
             finishTransaction = mock(),
-            finishCallback = {}
+            finishCallback = {},
         )
 
         verify(transitions)
@@ -784,7 +768,7 @@
                 any(),
                 any(),
                 any(),
-                eq(mixedHandler)
+                eq(mixedHandler),
             )
     }
 
@@ -814,14 +798,15 @@
 
     private fun createCloseTransitionInfo(
         @TransitionType type: Int,
-        changes: List<TransitionInfo.Change> = emptyList()
-    ): TransitionInfo = TransitionInfo(type, /* flags= */ 0).apply {
-        changes.forEach { change -> addChange(change) }
-    }
+        changes: List<TransitionInfo.Change> = emptyList(),
+    ): TransitionInfo =
+        TransitionInfo(type, /* flags= */ 0).apply {
+            changes.forEach { change -> addChange(change) }
+        }
 
     private fun createChange(
         task: RunningTaskInfo,
-        @TransitionInfo.TransitionMode mode: Int = TRANSIT_NONE
+        @TransitionInfo.TransitionMode mode: Int = TRANSIT_NONE,
     ): TransitionInfo.Change =
         TransitionInfo.Change(task.token, SurfaceControl()).apply {
             taskInfo = task
@@ -838,8 +823,6 @@
         RunningTaskInfo().apply {
             token = WindowContainerToken(mock<IWindowContainerToken>())
             baseIntent =
-                Intent().apply {
-                    component = DesktopWallpaperActivity.wallpaperActivityComponent
-                }
+                Intent().apply { component = DesktopWallpaperActivity.wallpaperActivityComponent }
         }
 }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt
index 2f225f2..abd7078 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt
@@ -53,9 +53,7 @@
 import org.mockito.kotlin.mock
 import org.mockito.kotlin.whenever
 
-/**
- * Tests for [DesktopModeEventLogger].
- */
+/** Tests for [DesktopModeEventLogger]. */
 class DesktopModeEventLoggerTest : ShellTestCase() {
 
     private val desktopModeEventLogger = DesktopModeEventLogger()
@@ -64,13 +62,13 @@
 
     @JvmField
     @Rule(order = 0)
-    val extendedMockitoRule = ExtendedMockitoRule.Builder(this)
-        .mockStatic(FrameworkStatsLog::class.java)
-        .mockStatic(EventLogTags::class.java).build()!!
+    val extendedMockitoRule =
+        ExtendedMockitoRule.Builder(this)
+            .mockStatic(FrameworkStatsLog::class.java)
+            .mockStatic(EventLogTags::class.java)
+            .build()!!
 
-    @JvmField
-    @Rule(order = 1)
-    val setFlagsRule = SetFlagsRule()
+    @JvmField @Rule(order = 1) val setFlagsRule = SetFlagsRule()
 
     @Before
     fun setUp() {
@@ -95,14 +93,14 @@
                 /* exit_reason */
                 eq(0),
                 /* sessionId */
-                eq(sessionId)
+                eq(sessionId),
             )
         }
         verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java))
         verify {
             EventLogTags.writeWmShellEnterDesktopMode(
                 eq(EnterReason.KEYBOARD_SHORTCUT_ENTER.reason),
-                eq(sessionId)
+                eq(sessionId),
             )
         }
         verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
@@ -127,14 +125,14 @@
                 /* exit_reason */
                 eq(0),
                 /* sessionId */
-                eq(sessionId)
+                eq(sessionId),
             )
         }
         verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java))
         verify {
             EventLogTags.writeWmShellEnterDesktopMode(
                 eq(EnterReason.KEYBOARD_SHORTCUT_ENTER.reason),
-                eq(sessionId)
+                eq(sessionId),
             )
         }
         verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
@@ -164,14 +162,14 @@
                 /* exit_reason */
                 eq(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EXIT_REASON__DRAG_TO_EXIT),
                 /* sessionId */
-                eq(sessionId)
+                eq(sessionId),
             )
         }
         verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java))
         verify {
             EventLogTags.writeWmShellExitDesktopMode(
                 eq(ExitReason.DRAG_TO_EXIT.reason),
-                eq(sessionId)
+                eq(sessionId),
             )
         }
         verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
@@ -214,16 +212,13 @@
                 eq(UNSET_MINIMIZE_REASON),
                 eq(UNSET_UNMINIMIZE_REASON),
                 /* visible_task_count */
-                eq(TASK_COUNT)
+                eq(TASK_COUNT),
             )
         }
         verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java))
         verify {
             EventLogTags.writeWmShellDesktopModeTaskUpdate(
-                eq(
-                    FrameworkStatsLog
-                        .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_ADDED
-                ),
+                eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_ADDED),
                 eq(TASK_UPDATE.instanceId),
                 eq(TASK_UPDATE.uid),
                 eq(TASK_UPDATE.taskHeight),
@@ -233,7 +228,7 @@
                 eq(sessionId),
                 eq(UNSET_MINIMIZE_REASON),
                 eq(UNSET_UNMINIMIZE_REASON),
-                eq(TASK_COUNT)
+                eq(TASK_COUNT),
             )
         }
         verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
@@ -275,16 +270,13 @@
                 eq(UNSET_MINIMIZE_REASON),
                 eq(UNSET_UNMINIMIZE_REASON),
                 /* visible_task_count */
-                eq(TASK_COUNT)
+                eq(TASK_COUNT),
             )
         }
         verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java))
         verify {
             EventLogTags.writeWmShellDesktopModeTaskUpdate(
-                eq(
-                    FrameworkStatsLog
-                        .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_REMOVED
-                ),
+                eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_REMOVED),
                 eq(TASK_UPDATE.instanceId),
                 eq(TASK_UPDATE.uid),
                 eq(TASK_UPDATE.taskHeight),
@@ -294,7 +286,7 @@
                 eq(sessionId),
                 eq(UNSET_MINIMIZE_REASON),
                 eq(UNSET_UNMINIMIZE_REASON),
-                eq(TASK_COUNT)
+                eq(TASK_COUNT),
             )
         }
         verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
@@ -339,7 +331,7 @@
                 eq(UNSET_MINIMIZE_REASON),
                 eq(UNSET_UNMINIMIZE_REASON),
                 /* visible_task_count */
-                eq(TASK_COUNT)
+                eq(TASK_COUNT),
             )
         }
         verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java))
@@ -358,7 +350,7 @@
                 eq(sessionId),
                 eq(UNSET_MINIMIZE_REASON),
                 eq(UNSET_UNMINIMIZE_REASON),
-                eq(TASK_COUNT)
+                eq(TASK_COUNT),
             )
         }
         verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
@@ -399,7 +391,7 @@
                 /* unminimize_reason */
                 eq(UNSET_UNMINIMIZE_REASON),
                 /* visible_task_count */
-                eq(TASK_COUNT)
+                eq(TASK_COUNT),
             )
         }
         verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java))
@@ -418,7 +410,7 @@
                 eq(sessionId),
                 eq(MinimizeReason.TASK_LIMIT.reason),
                 eq(UNSET_UNMINIMIZE_REASON),
-                eq(TASK_COUNT)
+                eq(TASK_COUNT),
             )
         }
         verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
@@ -459,7 +451,7 @@
                 /* unminimize_reason */
                 eq(UnminimizeReason.TASKBAR_TAP.reason),
                 /* visible_task_count */
-                eq(TASK_COUNT)
+                eq(TASK_COUNT),
             )
         }
         verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java))
@@ -478,7 +470,7 @@
                 eq(sessionId),
                 eq(UNSET_MINIMIZE_REASON),
                 eq(UnminimizeReason.TASKBAR_TAP.reason),
-                eq(TASK_COUNT)
+                eq(TASK_COUNT),
             )
         }
         verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
@@ -486,8 +478,11 @@
 
     @Test
     fun logTaskResizingStarted_noOngoingSession_doesNotLog() {
-        desktopModeEventLogger.logTaskResizingStarted(ResizeTrigger.CORNER,
-            InputMethod.UNKNOWN_INPUT_METHOD, createTaskInfo())
+        desktopModeEventLogger.logTaskResizingStarted(
+            ResizeTrigger.CORNER,
+            InputMethod.UNKNOWN_INPUT_METHOD,
+            createTaskInfo(),
+        )
 
         verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java))
         verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
@@ -498,19 +493,33 @@
     fun logTaskResizingStarted_logsTaskSizeUpdatedWithStartResizingStage() {
         val sessionId = startDesktopModeSession()
 
-        desktopModeEventLogger.logTaskResizingStarted(ResizeTrigger.CORNER,
-            InputMethod.UNKNOWN_INPUT_METHOD, createTaskInfo(), TASK_SIZE_UPDATE.taskWidth,
-            TASK_SIZE_UPDATE.taskHeight, displayController)
+        desktopModeEventLogger.logTaskResizingStarted(
+            ResizeTrigger.CORNER,
+            InputMethod.UNKNOWN_INPUT_METHOD,
+            createTaskInfo(),
+            TASK_SIZE_UPDATE.taskWidth,
+            TASK_SIZE_UPDATE.taskHeight,
+            displayController,
+        )
 
         verify {
             FrameworkStatsLog.write(
                 eq(FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED),
                 /* resize_trigger */
-                eq(FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED__RESIZE_TRIGGER__CORNER_RESIZE_TRIGGER),
+                eq(
+                    FrameworkStatsLog
+                        .DESKTOP_MODE_TASK_SIZE_UPDATED__RESIZE_TRIGGER__CORNER_RESIZE_TRIGGER
+                ),
                 /* resizing_stage */
-                eq(FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED__RESIZING_STAGE__START_RESIZING_STAGE),
+                eq(
+                    FrameworkStatsLog
+                        .DESKTOP_MODE_TASK_SIZE_UPDATED__RESIZING_STAGE__START_RESIZING_STAGE
+                ),
                 /* input_method */
-                eq(FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED__INPUT_METHOD__UNKNOWN_INPUT_METHOD),
+                eq(
+                    FrameworkStatsLog
+                        .DESKTOP_MODE_TASK_SIZE_UPDATED__INPUT_METHOD__UNKNOWN_INPUT_METHOD
+                ),
                 /* desktop_mode_session_id */
                 eq(sessionId),
                 /* instance_id */
@@ -530,8 +539,11 @@
 
     @Test
     fun logTaskResizingEnded_noOngoingSession_doesNotLog() {
-        desktopModeEventLogger.logTaskResizingEnded(ResizeTrigger.CORNER,
-            InputMethod.UNKNOWN_INPUT_METHOD, createTaskInfo())
+        desktopModeEventLogger.logTaskResizingEnded(
+            ResizeTrigger.CORNER,
+            InputMethod.UNKNOWN_INPUT_METHOD,
+            createTaskInfo(),
+        )
 
         verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java))
         verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
@@ -542,18 +554,31 @@
     fun logTaskResizingEnded_logsTaskSizeUpdatedWithEndResizingStage() {
         val sessionId = startDesktopModeSession()
 
-        desktopModeEventLogger.logTaskResizingEnded(ResizeTrigger.CORNER,
-            InputMethod.UNKNOWN_INPUT_METHOD, createTaskInfo(), displayController = displayController)
+        desktopModeEventLogger.logTaskResizingEnded(
+            ResizeTrigger.CORNER,
+            InputMethod.UNKNOWN_INPUT_METHOD,
+            createTaskInfo(),
+            displayController = displayController,
+        )
 
         verify {
             FrameworkStatsLog.write(
                 eq(FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED),
                 /* resize_trigger */
-                eq(FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED__RESIZE_TRIGGER__CORNER_RESIZE_TRIGGER),
+                eq(
+                    FrameworkStatsLog
+                        .DESKTOP_MODE_TASK_SIZE_UPDATED__RESIZE_TRIGGER__CORNER_RESIZE_TRIGGER
+                ),
                 /* resizing_stage */
-                eq(FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED__RESIZING_STAGE__END_RESIZING_STAGE),
+                eq(
+                    FrameworkStatsLog
+                        .DESKTOP_MODE_TASK_SIZE_UPDATED__RESIZING_STAGE__END_RESIZING_STAGE
+                ),
                 /* input_method */
-                eq(FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED__INPUT_METHOD__UNKNOWN_INPUT_METHOD),
+                eq(
+                    FrameworkStatsLog
+                        .DESKTOP_MODE_TASK_SIZE_UPDATED__INPUT_METHOD__UNKNOWN_INPUT_METHOD
+                ),
                 /* desktop_mode_session_id */
                 eq(sessionId),
                 /* instance_id */
@@ -582,9 +607,12 @@
     fun logTaskInfoStateInit_logsTaskInfoChangedStateInit() {
         desktopModeEventLogger.logTaskInfoStateInit()
         verify {
-            FrameworkStatsLog.write(eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE),
+            FrameworkStatsLog.write(
+                eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE),
                 /* task_event */
-                eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INIT_STATSD),
+                eq(
+                    FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INIT_STATSD
+                ),
                 /* instance_id */
                 eq(0),
                 /* uid */
@@ -604,13 +632,14 @@
                 /* unminimize_reason */
                 eq(UNSET_UNMINIMIZE_REASON),
                 /* visible_task_count */
-                eq(0)
+                eq(0),
             )
         }
     }
 
     private fun createTaskInfo(): RunningTaskInfo {
-        return TestRunningTaskInfoBuilder().setTaskId(TASK_ID)
+        return TestRunningTaskInfoBuilder()
+            .setTaskId(TASK_ID)
             .setUid(TASK_UID)
             .setBounds(Rect(TASK_X, TASK_Y, TASK_WIDTH, TASK_HEIGHT))
             .build()
@@ -628,27 +657,42 @@
         private const val DISPLAY_HEIGHT = 500
         private const val DISPLAY_AREA = DISPLAY_HEIGHT * DISPLAY_WIDTH
 
-        private val TASK_UPDATE = TaskUpdate(
-            TASK_ID, TASK_UID, TASK_HEIGHT, TASK_WIDTH, TASK_X, TASK_Y,
-            visibleTaskCount = TASK_COUNT,
-        )
+        private val TASK_UPDATE =
+            TaskUpdate(
+                TASK_ID,
+                TASK_UID,
+                TASK_HEIGHT,
+                TASK_WIDTH,
+                TASK_X,
+                TASK_Y,
+                visibleTaskCount = TASK_COUNT,
+            )
 
-        private val TASK_SIZE_UPDATE = TaskSizeUpdate(
-            resizeTrigger = ResizeTrigger.UNKNOWN_RESIZE_TRIGGER,
-            inputMethod = InputMethod.UNKNOWN_INPUT_METHOD,
-            TASK_ID,
-            TASK_UID,
-            TASK_HEIGHT,
-            TASK_WIDTH,
-            DISPLAY_AREA,
-        )
+        private val TASK_SIZE_UPDATE =
+            TaskSizeUpdate(
+                resizeTrigger = ResizeTrigger.UNKNOWN_RESIZE_TRIGGER,
+                inputMethod = InputMethod.UNKNOWN_INPUT_METHOD,
+                TASK_ID,
+                TASK_UID,
+                TASK_HEIGHT,
+                TASK_WIDTH,
+                DISPLAY_AREA,
+            )
 
         private fun createTaskUpdate(
             minimizeReason: MinimizeReason? = null,
             unminimizeReason: UnminimizeReason? = null,
-        ) = TaskUpdate(
-            TASK_ID, TASK_UID, TASK_HEIGHT, TASK_WIDTH, TASK_X, TASK_Y, minimizeReason,
-            unminimizeReason, TASK_COUNT
-        )
+        ) =
+            TaskUpdate(
+                TASK_ID,
+                TASK_UID,
+                TASK_HEIGHT,
+                TASK_WIDTH,
+                TASK_X,
+                TASK_Y,
+                minimizeReason,
+                unminimizeReason,
+                TASK_COUNT,
+            )
     }
 }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandlerTest.kt
index e57ae2a..413e7bc 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandlerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandlerTest.kt
@@ -30,49 +30,49 @@
 import android.view.KeyEvent
 import android.window.DisplayAreaInfo
 import androidx.test.filters.SmallTest
-import com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER
-import com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE
-import com.android.window.flags.Flags.FLAG_ENABLE_DISPLAY_FOCUS_IN_SHELL_TRANSITIONS
-import com.android.window.flags.Flags.FLAG_ENABLE_MOVE_TO_NEXT_DISPLAY_SHORTCUT
-import com.android.wm.shell.MockToken
-import com.android.wm.shell.RootTaskDisplayAreaOrganizer
-import com.android.wm.shell.ShellTaskOrganizer
-import com.android.wm.shell.ShellTestCase
-import com.android.wm.shell.desktopmode.DesktopTestHelpers.createFreeformTask
-import com.android.wm.shell.transition.FocusTransitionObserver
-import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.kotlin.eq
-import org.mockito.kotlin.whenever
 import com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer
 import com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn
 import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
 import com.android.dx.mockito.inline.extended.StaticMockitoSession
+import com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER
+import com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE
+import com.android.window.flags.Flags.FLAG_ENABLE_DISPLAY_FOCUS_IN_SHELL_TRANSITIONS
+import com.android.window.flags.Flags.FLAG_ENABLE_MOVE_TO_NEXT_DISPLAY_SHORTCUT
 import com.android.window.flags.Flags.FLAG_ENABLE_TASK_RESIZING_KEYBOARD_SHORTCUTS
+import com.android.wm.shell.MockToken
+import com.android.wm.shell.RootTaskDisplayAreaOrganizer
+import com.android.wm.shell.ShellTaskOrganizer
+import com.android.wm.shell.ShellTestCase
 import com.android.wm.shell.TestShellExecutor
 import com.android.wm.shell.common.DisplayController
 import com.android.wm.shell.common.DisplayLayout
+import com.android.wm.shell.desktopmode.DesktopTestHelpers.createFreeformTask
 import com.android.wm.shell.desktopmode.common.ToggleTaskSizeInteraction
 import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
 import com.android.wm.shell.sysui.ShellInit
+import com.android.wm.shell.transition.FocusTransitionObserver
 import com.android.wm.shell.windowdecor.DesktopModeWindowDecorViewModel
+import com.google.common.truth.Truth.assertThat
 import java.util.Optional
 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.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.Mockito.anyInt
 import org.mockito.Mockito.spy
 import org.mockito.Mockito.verify
 import org.mockito.kotlin.any
+import org.mockito.kotlin.eq
 import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
 import org.mockito.quality.Strictness
 
 /**
@@ -130,21 +130,24 @@
         whenever(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)).thenReturn(tda)
 
         doAnswer {
-            keyGestureEventHandler = (it.arguments[0] as KeyGestureEventHandler)
-            null
-        }.whenever(inputManager).registerKeyGestureEventHandler(any())
+                keyGestureEventHandler = (it.arguments[0] as KeyGestureEventHandler)
+                null
+            }
+            .whenever(inputManager)
+            .registerKeyGestureEventHandler(any())
         shellInit.init()
 
-        desktopModeKeyGestureHandler = DesktopModeKeyGestureHandler(
-            context,
-            Optional.of(desktopModeWindowDecorViewModel),
-            Optional.of(desktopTasksController),
-            inputManager,
-            shellTaskOrganizer,
-            focusTransitionObserver,
-            testExecutor,
-            displayController
-        )
+        desktopModeKeyGestureHandler =
+            DesktopModeKeyGestureHandler(
+                context,
+                Optional.of(desktopModeWindowDecorViewModel),
+                Optional.of(desktopTasksController),
+                inputManager,
+                shellTaskOrganizer,
+                focusTransitionObserver,
+                testExecutor,
+                displayController,
+            )
     }
 
     @After
@@ -160,7 +163,7 @@
     @EnableFlags(
         FLAG_ENABLE_DISPLAY_FOCUS_IN_SHELL_TRANSITIONS,
         FLAG_ENABLE_MOVE_TO_NEXT_DISPLAY_SHORTCUT,
-        FLAG_USE_KEY_GESTURE_EVENT_HANDLER
+        FLAG_USE_KEY_GESTURE_EVENT_HANDLER,
     )
     fun keyGestureMoveToNextDisplay_shouldMoveToNextDisplay() {
         // Set up two display ids
@@ -176,12 +179,13 @@
         whenever(shellTaskOrganizer.getRunningTasks()).thenReturn(arrayListOf(task))
         whenever(focusTransitionObserver.hasGlobalFocus(eq(task))).thenReturn(true)
 
-        val event = KeyGestureEvent.Builder()
-            .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_MOVE_TO_NEXT_DISPLAY)
-            .setDisplayId(SECOND_DISPLAY)
-            .setKeycodes(intArrayOf(KeyEvent.KEYCODE_D))
-            .setModifierState(KeyEvent.META_META_ON or KeyEvent.META_CTRL_ON)
-            .build()
+        val event =
+            KeyGestureEvent.Builder()
+                .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_MOVE_TO_NEXT_DISPLAY)
+                .setDisplayId(SECOND_DISPLAY)
+                .setKeycodes(intArrayOf(KeyEvent.KEYCODE_D))
+                .setModifierState(KeyEvent.META_META_ON or KeyEvent.META_CTRL_ON)
+                .build()
         val result = keyGestureEventHandler.handleKeyGestureEvent(event, null)
         testExecutor.flushAll()
 
@@ -190,108 +194,102 @@
     }
 
     @Test
-    @EnableFlags(
-        FLAG_USE_KEY_GESTURE_EVENT_HANDLER,
-        FLAG_ENABLE_TASK_RESIZING_KEYBOARD_SHORTCUTS
-    )
+    @EnableFlags(FLAG_USE_KEY_GESTURE_EVENT_HANDLER, FLAG_ENABLE_TASK_RESIZING_KEYBOARD_SHORTCUTS)
     fun keyGestureSnapLeft_shouldSnapResizeTaskToLeft() {
         val task = setUpFreeformTask()
         task.isFocused = true
         whenever(shellTaskOrganizer.getRunningTasks()).thenReturn(arrayListOf(task))
         whenever(focusTransitionObserver.hasGlobalFocus(eq(task))).thenReturn(true)
 
-        val event = KeyGestureEvent.Builder()
-            .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_SNAP_LEFT_FREEFORM_WINDOW)
-            .setKeycodes(intArrayOf(KeyEvent.KEYCODE_LEFT_BRACKET))
-            .setModifierState(KeyEvent.META_META_ON)
-            .build()
+        val event =
+            KeyGestureEvent.Builder()
+                .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_SNAP_LEFT_FREEFORM_WINDOW)
+                .setKeycodes(intArrayOf(KeyEvent.KEYCODE_LEFT_BRACKET))
+                .setModifierState(KeyEvent.META_META_ON)
+                .build()
         val result = keyGestureEventHandler.handleKeyGestureEvent(event, null)
         testExecutor.flushAll()
 
         assertThat(result).isTrue()
-        verify(desktopModeWindowDecorViewModel).onSnapResize(
-            task.taskId,
-            true,
-            DesktopModeEventLogger.Companion.InputMethod.KEYBOARD,
-            /* fromMenu= */ false
-        )
+        verify(desktopModeWindowDecorViewModel)
+            .onSnapResize(
+                task.taskId,
+                true,
+                DesktopModeEventLogger.Companion.InputMethod.KEYBOARD,
+                /* fromMenu= */ false,
+            )
     }
 
     @Test
-    @EnableFlags(
-        FLAG_USE_KEY_GESTURE_EVENT_HANDLER,
-        FLAG_ENABLE_TASK_RESIZING_KEYBOARD_SHORTCUTS
-    )
+    @EnableFlags(FLAG_USE_KEY_GESTURE_EVENT_HANDLER, FLAG_ENABLE_TASK_RESIZING_KEYBOARD_SHORTCUTS)
     fun keyGestureSnapRight_shouldSnapResizeTaskToRight() {
         val task = setUpFreeformTask()
         task.isFocused = true
         whenever(shellTaskOrganizer.getRunningTasks()).thenReturn(arrayListOf(task))
         whenever(focusTransitionObserver.hasGlobalFocus(eq(task))).thenReturn(true)
 
-        val event = KeyGestureEvent.Builder()
-            .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_SNAP_RIGHT_FREEFORM_WINDOW)
-            .setKeycodes(intArrayOf(KeyEvent.KEYCODE_RIGHT_BRACKET))
-            .setModifierState(KeyEvent.META_META_ON)
-            .build()
+        val event =
+            KeyGestureEvent.Builder()
+                .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_SNAP_RIGHT_FREEFORM_WINDOW)
+                .setKeycodes(intArrayOf(KeyEvent.KEYCODE_RIGHT_BRACKET))
+                .setModifierState(KeyEvent.META_META_ON)
+                .build()
         val result = keyGestureEventHandler.handleKeyGestureEvent(event, null)
         testExecutor.flushAll()
 
         assertThat(result).isTrue()
-        verify(desktopModeWindowDecorViewModel).onSnapResize(
-            task.taskId,
-            false,
-            DesktopModeEventLogger.Companion.InputMethod.KEYBOARD,
-            /* fromMenu= */ false
-        )
+        verify(desktopModeWindowDecorViewModel)
+            .onSnapResize(
+                task.taskId,
+                false,
+                DesktopModeEventLogger.Companion.InputMethod.KEYBOARD,
+                /* fromMenu= */ false,
+            )
     }
 
     @Test
-    @EnableFlags(
-        FLAG_USE_KEY_GESTURE_EVENT_HANDLER,
-        FLAG_ENABLE_TASK_RESIZING_KEYBOARD_SHORTCUTS
-    )
+    @EnableFlags(FLAG_USE_KEY_GESTURE_EVENT_HANDLER, FLAG_ENABLE_TASK_RESIZING_KEYBOARD_SHORTCUTS)
     fun keyGestureToggleFreeformWindowSize_shouldToggleTaskSize() {
         val task = setUpFreeformTask()
         task.isFocused = true
         whenever(shellTaskOrganizer.getRunningTasks()).thenReturn(arrayListOf(task))
         whenever(focusTransitionObserver.hasGlobalFocus(eq(task))).thenReturn(true)
 
-        val event = KeyGestureEvent.Builder()
-            .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MAXIMIZE_FREEFORM_WINDOW)
-            .setKeycodes(intArrayOf(KeyEvent.KEYCODE_EQUALS))
-            .setModifierState(KeyEvent.META_META_ON)
-            .build()
+        val event =
+            KeyGestureEvent.Builder()
+                .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MAXIMIZE_FREEFORM_WINDOW)
+                .setKeycodes(intArrayOf(KeyEvent.KEYCODE_EQUALS))
+                .setModifierState(KeyEvent.META_META_ON)
+                .build()
         val result = keyGestureEventHandler.handleKeyGestureEvent(event, null)
         testExecutor.flushAll()
 
         assertThat(result).isTrue()
-        verify(desktopTasksController).toggleDesktopTaskSize(
-            task,
-            ToggleTaskSizeInteraction(
-                isMaximized = isTaskMaximized(task, displayController),
-                source = ToggleTaskSizeInteraction.Source.KEYBOARD_SHORTCUT,
-                inputMethod =
-                    DesktopModeEventLogger.Companion.InputMethod.KEYBOARD,
-            ),
-        )
+        verify(desktopTasksController)
+            .toggleDesktopTaskSize(
+                task,
+                ToggleTaskSizeInteraction(
+                    isMaximized = isTaskMaximized(task, displayController),
+                    source = ToggleTaskSizeInteraction.Source.KEYBOARD_SHORTCUT,
+                    inputMethod = DesktopModeEventLogger.Companion.InputMethod.KEYBOARD,
+                ),
+            )
     }
 
     @Test
-    @EnableFlags(
-        FLAG_USE_KEY_GESTURE_EVENT_HANDLER,
-        FLAG_ENABLE_TASK_RESIZING_KEYBOARD_SHORTCUTS
-    )
+    @EnableFlags(FLAG_USE_KEY_GESTURE_EVENT_HANDLER, FLAG_ENABLE_TASK_RESIZING_KEYBOARD_SHORTCUTS)
     fun keyGestureMinimizeFreeformWindow_shouldMinimizeTask() {
         val task = setUpFreeformTask()
         task.isFocused = true
         whenever(shellTaskOrganizer.getRunningTasks()).thenReturn(arrayListOf(task))
         whenever(focusTransitionObserver.hasGlobalFocus(eq(task))).thenReturn(true)
 
-        val event = KeyGestureEvent.Builder()
-            .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_MINIMIZE_FREEFORM_WINDOW)
-            .setKeycodes(intArrayOf(KeyEvent.KEYCODE_MINUS))
-            .setModifierState(KeyEvent.META_META_ON)
-            .build()
+        val event =
+            KeyGestureEvent.Builder()
+                .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_MINIMIZE_FREEFORM_WINDOW)
+                .setKeycodes(intArrayOf(KeyEvent.KEYCODE_MINUS))
+                .setModifierState(KeyEvent.META_META_ON)
+                .build()
         val result = keyGestureEventHandler.handleKeyGestureEvent(event, null)
         testExecutor.flushAll()
 
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 7c4ce4a..43684fb 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
@@ -87,700 +87,735 @@
 @RunWith(AndroidTestingRunner::class)
 class DesktopModeLoggerTransitionObserverTest : ShellTestCase() {
 
-  @JvmField
-  @Rule
-  val extendedMockitoRule =
-      ExtendedMockitoRule.Builder(this)
-          .mockStatic(DesktopModeStatus::class.java)
-          .mockStatic(SystemProperties::class.java)
-          .mockStatic(Trace::class.java)
-          .build()!!
+    @JvmField
+    @Rule
+    val extendedMockitoRule =
+        ExtendedMockitoRule.Builder(this)
+            .mockStatic(DesktopModeStatus::class.java)
+            .mockStatic(SystemProperties::class.java)
+            .mockStatic(Trace::class.java)
+            .build()!!
 
-  private val testExecutor = mock<ShellExecutor>()
-  private val mockShellInit = mock<ShellInit>()
-  private val transitions = mock<Transitions>()
-  private val context = mock<Context>()
+    private val testExecutor = mock<ShellExecutor>()
+    private val mockShellInit = mock<ShellInit>()
+    private val transitions = mock<Transitions>()
+    private val context = mock<Context>()
 
-  private lateinit var transitionObserver: DesktopModeLoggerTransitionObserver
-  private lateinit var shellInit: ShellInit
-  private lateinit var desktopModeEventLogger: DesktopModeEventLogger
+    private lateinit var transitionObserver: DesktopModeLoggerTransitionObserver
+    private lateinit var shellInit: ShellInit
+    private lateinit var desktopModeEventLogger: DesktopModeEventLogger
 
-  @Before
-  fun setup() {
-    whenever(DesktopModeStatus.canEnterDesktopMode(any())).thenReturn(true)
-    shellInit = spy(ShellInit(testExecutor))
-    desktopModeEventLogger = mock<DesktopModeEventLogger>()
+    @Before
+    fun setup() {
+        whenever(DesktopModeStatus.canEnterDesktopMode(any())).thenReturn(true)
+        shellInit = spy(ShellInit(testExecutor))
+        desktopModeEventLogger = mock<DesktopModeEventLogger>()
 
-    transitionObserver = DesktopModeLoggerTransitionObserver(
-        context, mockShellInit, transitions, desktopModeEventLogger)
-    val initRunnableCaptor = ArgumentCaptor.forClass(Runnable::class.java)
-    verify(mockShellInit).addInitCallback(initRunnableCaptor.capture(), same(transitionObserver))
-    initRunnableCaptor.value.run()
-    // verify this initialisation interaction to leave the desktopmodeEventLogger mock in a
-    // consistent state with no outstanding interactions when test cases start executing.
-    verify(desktopModeEventLogger).logTaskInfoStateInit()
-  }
-
-  @Test
-  fun testInitialiseVisibleTasksSystemProperty() {
-    ExtendedMockito.verify {
-      SystemProperties.set(
-          eq(DesktopModeLoggerTransitionObserver.VISIBLE_TASKS_COUNTER_SYSTEM_PROPERTY),
-          eq(DesktopModeLoggerTransitionObserver
-              .VISIBLE_TASKS_COUNTER_SYSTEM_PROPERTY_DEFAULT_VALUE))
-    }
-  }
-
-  @Test
-  fun testRegistersObserverAtInit() {
-    verify(transitions).registerObserver(same(transitionObserver))
-  }
-
-  @Test
-  fun transitOpen_notFreeformWindow_doesNotLogTaskAddedOrSessionEnter() {
-    val change = createChange(TRANSIT_OPEN, createTaskInfo(WINDOWING_MODE_FULLSCREEN))
-    val transitionInfo = TransitionInfoBuilder(TRANSIT_OPEN, 0).addChange(change).build()
-
-    callOnTransitionReady(transitionInfo)
-
-    verify(desktopModeEventLogger, never()).logSessionEnter(any())
-    verify(desktopModeEventLogger, never()).logTaskAdded(any())
-  }
-
-  @Test
-  fun transitOpen_logTaskAddedAndEnterReasonAppFreeformIntent() {
-    val change = createChange(TRANSIT_OPEN, createTaskInfo(WINDOWING_MODE_FREEFORM))
-    val transitionInfo = TransitionInfoBuilder(TRANSIT_OPEN, 0).addChange(change).build()
-
-    callOnTransitionReady(transitionInfo)
-
-    verifyTaskAddedAndEnterLogging(EnterReason.APP_FREEFORM_INTENT,
-        DEFAULT_TASK_UPDATE.copy(visibleTaskCount = 1))
-  }
-
-  @Test
-  fun transitEndDragToDesktop_logTaskAddedAndEnterReasonAppHandleDrag() {
-    val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FREEFORM))
-    // task change is finalised when drag ends
-    val transitionInfo =
-        TransitionInfoBuilder(Transitions.TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP, 0)
-            .addChange(change)
-            .build()
-
-    callOnTransitionReady(transitionInfo)
-
-    verifyTaskAddedAndEnterLogging(EnterReason.APP_HANDLE_DRAG,
-        DEFAULT_TASK_UPDATE.copy(visibleTaskCount = 1))
-  }
-
-  @Test
-  fun transitEnterDesktopByButtonTap_logTaskAddedAndEnterReasonButtonTap() {
-    val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FREEFORM))
-    val transitionInfo =
-        TransitionInfoBuilder(TRANSIT_ENTER_DESKTOP_FROM_APP_HANDLE_MENU_BUTTON, 0)
-            .addChange(change)
-            .build()
-
-    callOnTransitionReady(transitionInfo)
-
-    verifyTaskAddedAndEnterLogging(EnterReason.APP_HANDLE_MENU_BUTTON,
-        DEFAULT_TASK_UPDATE.copy(visibleTaskCount = 1))
-  }
-
-  @Test
-  fun transitEnterDesktopFromAppFromOverview_logTaskAddedAndEnterReasonAppFromOverview() {
-    val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FREEFORM))
-    val transitionInfo =
-        TransitionInfoBuilder(TRANSIT_ENTER_DESKTOP_FROM_APP_FROM_OVERVIEW, 0)
-            .addChange(change)
-            .build()
-
-    callOnTransitionReady(transitionInfo)
-
-    verifyTaskAddedAndEnterLogging(EnterReason.APP_FROM_OVERVIEW,
-        DEFAULT_TASK_UPDATE.copy(visibleTaskCount = 1))
-  }
-
-  @Test
-  fun transitEnterDesktopFromKeyboardShortcut_logTaskAddedAndEnterReasonKeyboardShortcut() {
-    val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FREEFORM))
-    val transitionInfo =
-        TransitionInfoBuilder(TRANSIT_ENTER_DESKTOP_FROM_KEYBOARD_SHORTCUT, 0)
-            .addChange(change)
-            .build()
-
-    callOnTransitionReady(transitionInfo)
-
-    verifyTaskAddedAndEnterLogging(EnterReason.KEYBOARD_SHORTCUT_ENTER,
-        DEFAULT_TASK_UPDATE.copy(visibleTaskCount = 1))
-  }
-
-  @Test
-  fun transitToFront_logTaskAddedAndEnterReasonOverview() {
-    val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FREEFORM))
-    val transitionInfo = TransitionInfoBuilder(TRANSIT_TO_FRONT, 0).addChange(change).build()
-
-    callOnTransitionReady(transitionInfo)
-
-    verifyTaskAddedAndEnterLogging(EnterReason.OVERVIEW,
-        DEFAULT_TASK_UPDATE.copy(visibleTaskCount = 1))
-  }
-
-  @Test
-  fun transitToFront_previousTransitionExitToOverview_logTaskAddedAndEnterReasonOverview() {
-    // previous exit to overview transition
-    // add a freeform task
-    val previousTaskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM)
-    transitionObserver.addTaskInfosToCachedMap(previousTaskInfo)
-    transitionObserver.isSessionActive = true
-    val previousTransitionInfo =
-        TransitionInfoBuilder(TRANSIT_TO_FRONT, TRANSIT_FLAG_IS_RECENTS)
-            .addChange(createChange(TRANSIT_TO_BACK, previousTaskInfo))
-            .build()
-
-    callOnTransitionReady(previousTransitionInfo)
-
-    verifyTaskRemovedAndExitLogging(
-        ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE
-    )
-
-    // Enter desktop mode from cancelled recents has no transition. Enter is detected on the
-    // next transition involving freeform windows
-
-    // TRANSIT_TO_FRONT
-    val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FREEFORM))
-    val transitionInfo = TransitionInfoBuilder(TRANSIT_TO_FRONT, 0).addChange(change).build()
-
-    callOnTransitionReady(transitionInfo)
-
-    verifyTaskAddedAndEnterLogging(EnterReason.OVERVIEW,
-        DEFAULT_TASK_UPDATE.copy(visibleTaskCount = 1))
-  }
-
-  @Test
-  fun transitChange_previousTransitionExitToOverview_logTaskAddedAndEnterReasonOverview() {
-    // previous exit to overview transition
-    // add a freeform task
-    val previousTaskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM)
-    transitionObserver.addTaskInfosToCachedMap(previousTaskInfo)
-    transitionObserver.isSessionActive = true
-    val previousTransitionInfo =
-        TransitionInfoBuilder(TRANSIT_TO_FRONT, TRANSIT_FLAG_IS_RECENTS)
-            .addChange(createChange(TRANSIT_TO_BACK, previousTaskInfo))
-            .build()
-
-    callOnTransitionReady(previousTransitionInfo)
-
-    verifyTaskRemovedAndExitLogging(
-        ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE
-    )
-
-    // Enter desktop mode from cancelled recents has no transition. Enter is detected on the
-    // next transition involving freeform windows
-
-    // TRANSIT_CHANGE
-    val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FREEFORM))
-    val transitionInfo = TransitionInfoBuilder(TRANSIT_CHANGE, 0).addChange(change).build()
-
-    callOnTransitionReady(transitionInfo)
-
-    verifyTaskAddedAndEnterLogging(EnterReason.OVERVIEW,
-        DEFAULT_TASK_UPDATE.copy(visibleTaskCount = 1))
-  }
-
-  @Test
-  fun transitOpen_previousTransitionExitToOverview_logTaskAddedAndEnterReasonOverview() {
-    // previous exit to overview transition
-    // add a freeform task
-    val previousTaskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM)
-    transitionObserver.addTaskInfosToCachedMap(previousTaskInfo)
-    transitionObserver.isSessionActive = true
-    val previousTransitionInfo =
-        TransitionInfoBuilder(TRANSIT_TO_FRONT, TRANSIT_FLAG_IS_RECENTS)
-            .addChange(createChange(TRANSIT_TO_BACK, previousTaskInfo))
-            .build()
-
-    callOnTransitionReady(previousTransitionInfo)
-
-    verifyTaskRemovedAndExitLogging(
-        ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE
-    )
-
-    // Enter desktop mode from cancelled recents has no transition. Enter is detected on the
-    // next transition involving freeform windows
-
-    // TRANSIT_OPEN
-    val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FREEFORM))
-    val transitionInfo = TransitionInfoBuilder(TRANSIT_OPEN, 0).addChange(change).build()
-
-    callOnTransitionReady(transitionInfo)
-
-    verifyTaskAddedAndEnterLogging(EnterReason.OVERVIEW,
-        DEFAULT_TASK_UPDATE.copy(visibleTaskCount = 1))
-  }
-
-  @Test
-  @Suppress("ktlint:standard:max-line-length")
-  fun transitEnterDesktopFromAppFromOverview_previousTransitionExitToOverview_logTaskAddedAndEnterReasonAppFromOverview() {
-    // Tests for AppFromOverview precedence in compared to cancelled Overview
-
-    // previous exit to overview transition
-    // add a freeform task
-    val previousTaskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM)
-    transitionObserver.addTaskInfosToCachedMap(previousTaskInfo)
-    transitionObserver.isSessionActive = true
-    val previousTransitionInfo =
-        TransitionInfoBuilder(TRANSIT_TO_FRONT, TRANSIT_FLAG_IS_RECENTS)
-            .addChange(createChange(TRANSIT_TO_BACK, previousTaskInfo))
-            .build()
-
-    callOnTransitionReady(previousTransitionInfo)
-
-    verifyTaskRemovedAndExitLogging(
-        ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE
-    )
-
-    // TRANSIT_ENTER_DESKTOP_FROM_APP_FROM_OVERVIEW
-    val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FREEFORM))
-    val transitionInfo =
-        TransitionInfoBuilder(TRANSIT_ENTER_DESKTOP_FROM_APP_FROM_OVERVIEW, 0)
-            .addChange(change)
-            .build()
-
-    callOnTransitionReady(transitionInfo)
-
-    verifyTaskAddedAndEnterLogging(EnterReason.APP_FROM_OVERVIEW,
-        DEFAULT_TASK_UPDATE.copy(visibleTaskCount = 1))
-  }
-
-  @Test
-  fun transitEnterDesktopFromUnknown_logTaskAddedAndEnterReasonUnknown() {
-    val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FREEFORM))
-    val transitionInfo =
-        TransitionInfoBuilder(TRANSIT_ENTER_DESKTOP_FROM_UNKNOWN, 0).addChange(change).build()
-
-    callOnTransitionReady(transitionInfo)
-
-    verifyTaskAddedAndEnterLogging(EnterReason.UNKNOWN_ENTER,
-        DEFAULT_TASK_UPDATE.copy(visibleTaskCount = 1))
-  }
-
-  @Test
-  fun transitWake_logTaskAddedAndEnterReasonScreenOn() {
-    val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FREEFORM))
-    val transitionInfo = TransitionInfoBuilder(TRANSIT_WAKE, 0).addChange(change).build()
-
-    callOnTransitionReady(transitionInfo)
-
-    verifyTaskAddedAndEnterLogging(EnterReason.SCREEN_ON,
-        DEFAULT_TASK_UPDATE.copy(visibleTaskCount = 1))
-  }
-
-  @Test
-  fun transitBack_previousExitReasonScreenOff_logTaskAddedAndEnterReasonScreenOn() {
-    val freeformTask = createTaskInfo(WINDOWING_MODE_FREEFORM)
-    // Previous Exit reason recorded as Screen Off
-    transitionObserver.addTaskInfosToCachedMap(freeformTask)
-    transitionObserver.isSessionActive = true
-    callOnTransitionReady(TransitionInfoBuilder(TRANSIT_SLEEP).build())
-    verifyTaskRemovedAndExitLogging(ExitReason.SCREEN_OFF, DEFAULT_TASK_UPDATE)
-    // Enter desktop through back transition, this happens when user enters after dismissing
-    // keyguard
-    val change = createChange(TRANSIT_TO_FRONT, freeformTask)
-    val transitionInfo = TransitionInfoBuilder(TRANSIT_TO_BACK, 0).addChange(change).build()
-
-    callOnTransitionReady(transitionInfo)
-
-    verifyTaskAddedAndEnterLogging(EnterReason.SCREEN_ON,
-        DEFAULT_TASK_UPDATE.copy(visibleTaskCount = 1))
-  }
-
-  @Test
-  fun transitEndDragToDesktop_previousExitReasonScreenOff_logTaskAddedAndEnterReasonAppDrag() {
-    val freeformTask = createTaskInfo(WINDOWING_MODE_FREEFORM)
-    // Previous Exit reason recorded as Screen Off
-    transitionObserver.addTaskInfosToCachedMap(freeformTask)
-    transitionObserver.isSessionActive = true
-    callOnTransitionReady(TransitionInfoBuilder(TRANSIT_SLEEP).build())
-    verifyTaskRemovedAndExitLogging(ExitReason.SCREEN_OFF, DEFAULT_TASK_UPDATE)
-
-    // Enter desktop through app handle drag. This represents cases where instead of moving to
-    // desktop right after turning the screen on, we move to fullscreen then move another task
-    // to desktop
-    val transitionInfo =
-        TransitionInfoBuilder(Transitions.TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP, 0)
-            .addChange(createChange(TRANSIT_TO_FRONT, freeformTask))
-            .build()
-    callOnTransitionReady(transitionInfo)
-
-    verifyTaskAddedAndEnterLogging(EnterReason.APP_HANDLE_DRAG,
-        DEFAULT_TASK_UPDATE.copy(visibleTaskCount = 1))
-  }
-
-  @Test
-  fun transitSleep_logTaskRemovedAndExitReasonScreenOff() {
-    // add a freeform task
-    transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM))
-    transitionObserver.isSessionActive = true
-
-    val transitionInfo = TransitionInfoBuilder(TRANSIT_SLEEP).build()
-    callOnTransitionReady(transitionInfo)
-
-    verifyTaskRemovedAndExitLogging(ExitReason.SCREEN_OFF, DEFAULT_TASK_UPDATE)
-  }
-
-  @Test
-  fun transitExitDesktopTaskDrag_logTaskRemovedAndExitReasonDragToExit() {
-    // add a freeform task
-    transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM))
-    transitionObserver.isSessionActive = true
-
-    // window mode changing from FREEFORM to FULLSCREEN
-    val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FULLSCREEN))
-    val transitionInfo =
-        TransitionInfoBuilder(TRANSIT_EXIT_DESKTOP_MODE_TASK_DRAG).addChange(change).build()
-    callOnTransitionReady(transitionInfo)
-
-    verifyTaskRemovedAndExitLogging(ExitReason.DRAG_TO_EXIT, DEFAULT_TASK_UPDATE)
-  }
-
-  @Test
-  fun transitExitDesktopAppHandleButton_logTaskRemovedAndExitReasonButton() {
-    // add a freeform task
-    transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM))
-    transitionObserver.isSessionActive = true
-
-    // window mode changing from FREEFORM to FULLSCREEN
-    val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FULLSCREEN))
-    val transitionInfo =
-        TransitionInfoBuilder(TRANSIT_EXIT_DESKTOP_MODE_HANDLE_MENU_BUTTON)
-            .addChange(change)
-            .build()
-    callOnTransitionReady(transitionInfo)
-
-    verifyTaskRemovedAndExitLogging(ExitReason.APP_HANDLE_MENU_BUTTON_EXIT, DEFAULT_TASK_UPDATE)
-  }
-
-  @Test
-  fun transitExitDesktopUsingKeyboard_logTaskRemovedAndExitReasonKeyboard() {
-    // add a freeform task
-    transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM))
-    transitionObserver.isSessionActive = true
-
-    // window mode changing from FREEFORM to FULLSCREEN
-    val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FULLSCREEN))
-    val transitionInfo =
-        TransitionInfoBuilder(TRANSIT_EXIT_DESKTOP_MODE_KEYBOARD_SHORTCUT).addChange(change).build()
-    callOnTransitionReady(transitionInfo)
-
-    verifyTaskRemovedAndExitLogging(ExitReason.KEYBOARD_SHORTCUT_EXIT, DEFAULT_TASK_UPDATE)
-  }
-
-  @Test
-  fun transitExitDesktopUnknown_logTaskRemovedAndExitReasonUnknown() {
-    // add a freeform task
-    transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM))
-    transitionObserver.isSessionActive = true
-
-    // window mode changing from FREEFORM to FULLSCREEN
-    val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FULLSCREEN))
-    val transitionInfo =
-        TransitionInfoBuilder(TRANSIT_EXIT_DESKTOP_MODE_UNKNOWN).addChange(change).build()
-    callOnTransitionReady(transitionInfo)
-
-    verifyTaskRemovedAndExitLogging(ExitReason.UNKNOWN_EXIT, DEFAULT_TASK_UPDATE)
-  }
-
-  @Test
-  fun transitToFrontWithFlagRecents_logTaskRemovedAndExitReasonOverview() {
-    // add a freeform task
-    transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM))
-    transitionObserver.isSessionActive = true
-
-    // recents transition
-    val change = createChange(TRANSIT_TO_BACK, createTaskInfo(WINDOWING_MODE_FREEFORM))
-    val transitionInfo =
-        TransitionInfoBuilder(TRANSIT_TO_FRONT, TRANSIT_FLAG_IS_RECENTS).addChange(change).build()
-    callOnTransitionReady(transitionInfo)
-
-    verifyTaskRemovedAndExitLogging(
-        ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE
-    )
-  }
-
-  @Test
-  fun transitClose_logTaskRemovedAndExitReasonTaskFinished() {
-    // add a freeform task
-    transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM))
-    transitionObserver.isSessionActive = true
-
-    // task closing
-    val change = createChange(TRANSIT_CLOSE, createTaskInfo(WINDOWING_MODE_FULLSCREEN))
-    val transitionInfo = TransitionInfoBuilder(TRANSIT_CLOSE).addChange(change).build()
-    callOnTransitionReady(transitionInfo)
-
-    verifyTaskRemovedAndExitLogging(ExitReason.TASK_FINISHED, DEFAULT_TASK_UPDATE)
-  }
-
-  @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)
-    transitionObserver.addTaskInfosToCachedMap(taskInfo)
-    transitionObserver.isSessionActive = true
-
-    // recents transition sent freeform window to back
-    val change = createChange(TRANSIT_TO_BACK, taskInfo)
-    val transitionInfo1 =
-        TransitionInfoBuilder(TRANSIT_TO_FRONT, TRANSIT_FLAG_IS_RECENTS).addChange(change).build()
-    callOnTransitionReady(transitionInfo1)
-
-    verifyTaskRemovedAndExitLogging(
-        ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE
-    )
-
-    val transitionInfo2 = TransitionInfoBuilder(TRANSIT_NONE).build()
-    callOnTransitionReady(transitionInfo2)
-
-    verifyTaskAddedAndEnterLogging(EnterReason.OVERVIEW,
-        DEFAULT_TASK_UPDATE.copy(visibleTaskCount = 1))
-  }
-
-  @Test
-  fun sessionAlreadyStarted_newFreeformTaskAdded_logsTaskAdded() {
-    // add an existing freeform task
-    transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM))
-    transitionObserver.isSessionActive = true
-
-    // new freeform task added
-    val change = createChange(TRANSIT_OPEN, createTaskInfo(WINDOWING_MODE_FREEFORM, id = 2))
-    val transitionInfo = TransitionInfoBuilder(TRANSIT_OPEN, 0).addChange(change).build()
-    callOnTransitionReady(transitionInfo)
-
-    verify(desktopModeEventLogger, times(1))
-        .logTaskAdded(eq(DEFAULT_TASK_UPDATE.copy(instanceId = 2, visibleTaskCount = 2)))
-    verify(desktopModeEventLogger, never()).logSessionEnter(any())
-  }
-
-  @Test
-  fun sessionAlreadyStarted_taskPositionChanged_logsTaskUpdate() {
-    // add an existing freeform task
-    val taskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM)
-    transitionObserver.addTaskInfosToCachedMap(taskInfo)
-    transitionObserver.isSessionActive = true
-
-    // task position changed
-    val newTaskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM, taskX = DEFAULT_TASK_X + 100)
-    val transitionInfo =
-        TransitionInfoBuilder(TRANSIT_CHANGE, 0)
-            .addChange(createChange(TRANSIT_CHANGE, newTaskInfo))
-            .build()
-    callOnTransitionReady(transitionInfo)
-
-    verify(desktopModeEventLogger, times(1))
-        .logTaskInfoChanged(
-            eq(DEFAULT_TASK_UPDATE.copy(taskX = DEFAULT_TASK_X + 100, visibleTaskCount = 1))
-        )
-    verifyZeroInteractions(desktopModeEventLogger)
-  }
-
-  @Test
-  fun sessionAlreadyStarted_taskResized_logsTaskUpdate() {
-    // add an existing freeform task
-    val taskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM)
-    transitionObserver.addTaskInfosToCachedMap(taskInfo)
-    transitionObserver.isSessionActive = true
-
-    // task resized
-    val newTaskInfo =
-        createTaskInfo(
-            WINDOWING_MODE_FREEFORM,
-            taskWidth = DEFAULT_TASK_WIDTH + 100,
-            taskHeight = DEFAULT_TASK_HEIGHT - 100)
-    val transitionInfo =
-        TransitionInfoBuilder(TRANSIT_CHANGE, 0)
-            .addChange(createChange(TRANSIT_CHANGE, newTaskInfo))
-            .build()
-    callOnTransitionReady(transitionInfo)
-
-    verify(desktopModeEventLogger, times(1))
-        .logTaskInfoChanged(
-            eq(
-                DEFAULT_TASK_UPDATE.copy(
-                    taskWidth = DEFAULT_TASK_WIDTH + 100, taskHeight = DEFAULT_TASK_HEIGHT - 100,
-                    visibleTaskCount = 1))
-        )
-    verifyZeroInteractions(desktopModeEventLogger)
-  }
-
-  @Test
-  fun sessionAlreadyStarted_multipleTasksUpdated_logsTaskUpdateForCorrectTask() {
-    // add 2 existing freeform task
-    val taskInfo1 = createTaskInfo(WINDOWING_MODE_FREEFORM)
-    val taskInfo2 = createTaskInfo(WINDOWING_MODE_FREEFORM, id = 2)
-    transitionObserver.addTaskInfosToCachedMap(taskInfo1)
-    transitionObserver.addTaskInfosToCachedMap(taskInfo2)
-    transitionObserver.isSessionActive = true
-
-    // task 1 position update
-    val newTaskInfo1 = createTaskInfo(WINDOWING_MODE_FREEFORM, taskX = DEFAULT_TASK_X + 100)
-    val transitionInfo1 =
-        TransitionInfoBuilder(TRANSIT_CHANGE, 0)
-            .addChange(createChange(TRANSIT_CHANGE, newTaskInfo1))
-            .build()
-    callOnTransitionReady(transitionInfo1)
-
-    verify(desktopModeEventLogger, times(1))
-        .logTaskInfoChanged(
-            eq(DEFAULT_TASK_UPDATE.copy(
-                taskX = DEFAULT_TASK_X + 100, visibleTaskCount = 2))
-        )
-    verifyZeroInteractions(desktopModeEventLogger)
-
-    // task 2 resize
-    val newTaskInfo2 =
-        createTaskInfo(
-            WINDOWING_MODE_FREEFORM,
-            id = 2,
-            taskWidth = DEFAULT_TASK_WIDTH + 100,
-            taskHeight = DEFAULT_TASK_HEIGHT - 100)
-    val transitionInfo2 =
-        TransitionInfoBuilder(TRANSIT_CHANGE, 0)
-            .addChange(createChange(TRANSIT_CHANGE, newTaskInfo2))
-            .build()
-
-    callOnTransitionReady(transitionInfo2)
-
-    verify(desktopModeEventLogger, times(1))
-        .logTaskInfoChanged(
-            eq(
-                DEFAULT_TASK_UPDATE.copy(
-                    instanceId = 2,
-                    taskWidth = DEFAULT_TASK_WIDTH + 100,
-                    taskHeight = DEFAULT_TASK_HEIGHT - 100,
-                    visibleTaskCount = 2)),
+        transitionObserver =
+            DesktopModeLoggerTransitionObserver(
+                context,
+                mockShellInit,
+                transitions,
+                desktopModeEventLogger,
             )
-    verifyZeroInteractions(desktopModeEventLogger)
-  }
-
-  @Test
-  fun sessionAlreadyStarted_freeformTaskRemoved_logsTaskRemoved() {
-    // add two existing freeform tasks
-    transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM))
-    transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM, id = 2))
-    transitionObserver.isSessionActive = true
-
-    // new freeform task closed
-    val change = createChange(TRANSIT_CLOSE, createTaskInfo(WINDOWING_MODE_FREEFORM, id = 2))
-    val transitionInfo = TransitionInfoBuilder(TRANSIT_CLOSE, 0).addChange(change).build()
-    callOnTransitionReady(transitionInfo)
-
-    verify(desktopModeEventLogger, times(1))
-        .logTaskRemoved(
-            eq(DEFAULT_TASK_UPDATE.copy(
-                instanceId = 2, visibleTaskCount = 1))
-        )
-    verify(desktopModeEventLogger, never()).logSessionExit(any())
-  }
-
-  /** Simulate calling the onTransitionReady() method */
-  private fun callOnTransitionReady(transitionInfo: TransitionInfo) {
-    val transition = mock<IBinder>()
-    val startT = mock<SurfaceControl.Transaction>()
-    val finishT = mock<SurfaceControl.Transaction>()
-
-    transitionObserver.onTransitionReady(transition, transitionInfo, startT, finishT)
-  }
-
-  private fun verifyTaskAddedAndEnterLogging(enterReason: EnterReason, taskUpdate: TaskUpdate) {
-    assertTrue(transitionObserver.isSessionActive)
-    verify(desktopModeEventLogger, times(1)).logSessionEnter(eq(enterReason))
-    verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(taskUpdate))
-    ExtendedMockito.verify {
-        Trace.setCounter(
-            eq(Trace.TRACE_TAG_WINDOW_MANAGER),
-            eq(DesktopModeLoggerTransitionObserver.VISIBLE_TASKS_COUNTER_NAME),
-            eq(taskUpdate.visibleTaskCount.toLong()))
+        val initRunnableCaptor = ArgumentCaptor.forClass(Runnable::class.java)
+        verify(mockShellInit)
+            .addInitCallback(initRunnableCaptor.capture(), same(transitionObserver))
+        initRunnableCaptor.value.run()
+        // verify this initialisation interaction to leave the desktopmodeEventLogger mock in a
+        // consistent state with no outstanding interactions when test cases start executing.
+        verify(desktopModeEventLogger).logTaskInfoStateInit()
     }
-    ExtendedMockito.verify {
-        SystemProperties.set(
-            eq(DesktopModeLoggerTransitionObserver.VISIBLE_TASKS_COUNTER_SYSTEM_PROPERTY),
-            eq(taskUpdate.visibleTaskCount.toString()))
-    }
-    verifyZeroInteractions(desktopModeEventLogger)
-  }
 
-  private fun verifyTaskRemovedAndExitLogging(
-      exitReason: ExitReason,
-      taskUpdate: TaskUpdate
-  ) {
-    assertFalse(transitionObserver.isSessionActive)
-    verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(taskUpdate))
-    verify(desktopModeEventLogger, times(1)).logSessionExit(eq(exitReason))
-    verifyZeroInteractions(desktopModeEventLogger)
-  }
-
-  private companion object {
-    const val DEFAULT_TASK_ID = 1
-    const val DEFAULT_TASK_UID = 2
-    const val DEFAULT_TASK_HEIGHT = 100
-    const val DEFAULT_TASK_WIDTH = 200
-    const val DEFAULT_TASK_X = 30
-    const val DEFAULT_TASK_Y = 70
-    const val DEFAULT_VISIBLE_TASK_COUNT = 0
-    val DEFAULT_TASK_UPDATE =
-        TaskUpdate(
-            DEFAULT_TASK_ID,
-            DEFAULT_TASK_UID,
-            DEFAULT_TASK_HEIGHT,
-            DEFAULT_TASK_WIDTH,
-            DEFAULT_TASK_X,
-            DEFAULT_TASK_Y,
-            visibleTaskCount = DEFAULT_VISIBLE_TASK_COUNT,
-        )
-
-    fun createTaskInfo(
-        windowMode: Int,
-        id: Int = DEFAULT_TASK_ID,
-        uid: Int = DEFAULT_TASK_UID,
-        taskHeight: Int = DEFAULT_TASK_HEIGHT,
-        taskWidth: Int = DEFAULT_TASK_WIDTH,
-        taskX: Int = DEFAULT_TASK_X,
-        taskY: Int = DEFAULT_TASK_Y,
-    ) =
-        ActivityManager.RunningTaskInfo().apply {
-          taskId = id
-          effectiveUid = uid
-          configuration.windowConfiguration.apply {
-            windowingMode = windowMode
-            positionInParent = Point(taskX, taskY)
-            bounds.set(Rect(taskX, taskY, taskX + taskWidth, taskY + taskHeight))
-          }
+    @Test
+    fun testInitialiseVisibleTasksSystemProperty() {
+        ExtendedMockito.verify {
+            SystemProperties.set(
+                eq(DesktopModeLoggerTransitionObserver.VISIBLE_TASKS_COUNTER_SYSTEM_PROPERTY),
+                eq(
+                    DesktopModeLoggerTransitionObserver
+                        .VISIBLE_TASKS_COUNTER_SYSTEM_PROPERTY_DEFAULT_VALUE
+                ),
+            )
         }
-
-    fun createChange(mode: Int, taskInfo: ActivityManager.RunningTaskInfo): Change {
-      val change =
-          Change(WindowContainerToken(mock<IWindowContainerToken>()), mock<SurfaceControl>())
-      change.mode = mode
-      change.taskInfo = taskInfo
-      return change
     }
-  }
+
+    @Test
+    fun testRegistersObserverAtInit() {
+        verify(transitions).registerObserver(same(transitionObserver))
+    }
+
+    @Test
+    fun transitOpen_notFreeformWindow_doesNotLogTaskAddedOrSessionEnter() {
+        val change = createChange(TRANSIT_OPEN, createTaskInfo(WINDOWING_MODE_FULLSCREEN))
+        val transitionInfo = TransitionInfoBuilder(TRANSIT_OPEN, 0).addChange(change).build()
+
+        callOnTransitionReady(transitionInfo)
+
+        verify(desktopModeEventLogger, never()).logSessionEnter(any())
+        verify(desktopModeEventLogger, never()).logTaskAdded(any())
+    }
+
+    @Test
+    fun transitOpen_logTaskAddedAndEnterReasonAppFreeformIntent() {
+        val change = createChange(TRANSIT_OPEN, createTaskInfo(WINDOWING_MODE_FREEFORM))
+        val transitionInfo = TransitionInfoBuilder(TRANSIT_OPEN, 0).addChange(change).build()
+
+        callOnTransitionReady(transitionInfo)
+
+        verifyTaskAddedAndEnterLogging(
+            EnterReason.APP_FREEFORM_INTENT,
+            DEFAULT_TASK_UPDATE.copy(visibleTaskCount = 1),
+        )
+    }
+
+    @Test
+    fun transitEndDragToDesktop_logTaskAddedAndEnterReasonAppHandleDrag() {
+        val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FREEFORM))
+        // task change is finalised when drag ends
+        val transitionInfo =
+            TransitionInfoBuilder(Transitions.TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP, 0)
+                .addChange(change)
+                .build()
+
+        callOnTransitionReady(transitionInfo)
+
+        verifyTaskAddedAndEnterLogging(
+            EnterReason.APP_HANDLE_DRAG,
+            DEFAULT_TASK_UPDATE.copy(visibleTaskCount = 1),
+        )
+    }
+
+    @Test
+    fun transitEnterDesktopByButtonTap_logTaskAddedAndEnterReasonButtonTap() {
+        val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FREEFORM))
+        val transitionInfo =
+            TransitionInfoBuilder(TRANSIT_ENTER_DESKTOP_FROM_APP_HANDLE_MENU_BUTTON, 0)
+                .addChange(change)
+                .build()
+
+        callOnTransitionReady(transitionInfo)
+
+        verifyTaskAddedAndEnterLogging(
+            EnterReason.APP_HANDLE_MENU_BUTTON,
+            DEFAULT_TASK_UPDATE.copy(visibleTaskCount = 1),
+        )
+    }
+
+    @Test
+    fun transitEnterDesktopFromAppFromOverview_logTaskAddedAndEnterReasonAppFromOverview() {
+        val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FREEFORM))
+        val transitionInfo =
+            TransitionInfoBuilder(TRANSIT_ENTER_DESKTOP_FROM_APP_FROM_OVERVIEW, 0)
+                .addChange(change)
+                .build()
+
+        callOnTransitionReady(transitionInfo)
+
+        verifyTaskAddedAndEnterLogging(
+            EnterReason.APP_FROM_OVERVIEW,
+            DEFAULT_TASK_UPDATE.copy(visibleTaskCount = 1),
+        )
+    }
+
+    @Test
+    fun transitEnterDesktopFromKeyboardShortcut_logTaskAddedAndEnterReasonKeyboardShortcut() {
+        val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FREEFORM))
+        val transitionInfo =
+            TransitionInfoBuilder(TRANSIT_ENTER_DESKTOP_FROM_KEYBOARD_SHORTCUT, 0)
+                .addChange(change)
+                .build()
+
+        callOnTransitionReady(transitionInfo)
+
+        verifyTaskAddedAndEnterLogging(
+            EnterReason.KEYBOARD_SHORTCUT_ENTER,
+            DEFAULT_TASK_UPDATE.copy(visibleTaskCount = 1),
+        )
+    }
+
+    @Test
+    fun transitToFront_logTaskAddedAndEnterReasonOverview() {
+        val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FREEFORM))
+        val transitionInfo = TransitionInfoBuilder(TRANSIT_TO_FRONT, 0).addChange(change).build()
+
+        callOnTransitionReady(transitionInfo)
+
+        verifyTaskAddedAndEnterLogging(
+            EnterReason.OVERVIEW,
+            DEFAULT_TASK_UPDATE.copy(visibleTaskCount = 1),
+        )
+    }
+
+    @Test
+    fun transitToFront_previousTransitionExitToOverview_logTaskAddedAndEnterReasonOverview() {
+        // previous exit to overview transition
+        // add a freeform task
+        val previousTaskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM)
+        transitionObserver.addTaskInfosToCachedMap(previousTaskInfo)
+        transitionObserver.isSessionActive = true
+        val previousTransitionInfo =
+            TransitionInfoBuilder(TRANSIT_TO_FRONT, TRANSIT_FLAG_IS_RECENTS)
+                .addChange(createChange(TRANSIT_TO_BACK, previousTaskInfo))
+                .build()
+
+        callOnTransitionReady(previousTransitionInfo)
+
+        verifyTaskRemovedAndExitLogging(ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE)
+
+        // Enter desktop mode from cancelled recents has no transition. Enter is detected on the
+        // next transition involving freeform windows
+
+        // TRANSIT_TO_FRONT
+        val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FREEFORM))
+        val transitionInfo = TransitionInfoBuilder(TRANSIT_TO_FRONT, 0).addChange(change).build()
+
+        callOnTransitionReady(transitionInfo)
+
+        verifyTaskAddedAndEnterLogging(
+            EnterReason.OVERVIEW,
+            DEFAULT_TASK_UPDATE.copy(visibleTaskCount = 1),
+        )
+    }
+
+    @Test
+    fun transitChange_previousTransitionExitToOverview_logTaskAddedAndEnterReasonOverview() {
+        // previous exit to overview transition
+        // add a freeform task
+        val previousTaskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM)
+        transitionObserver.addTaskInfosToCachedMap(previousTaskInfo)
+        transitionObserver.isSessionActive = true
+        val previousTransitionInfo =
+            TransitionInfoBuilder(TRANSIT_TO_FRONT, TRANSIT_FLAG_IS_RECENTS)
+                .addChange(createChange(TRANSIT_TO_BACK, previousTaskInfo))
+                .build()
+
+        callOnTransitionReady(previousTransitionInfo)
+
+        verifyTaskRemovedAndExitLogging(ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE)
+
+        // Enter desktop mode from cancelled recents has no transition. Enter is detected on the
+        // next transition involving freeform windows
+
+        // TRANSIT_CHANGE
+        val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FREEFORM))
+        val transitionInfo = TransitionInfoBuilder(TRANSIT_CHANGE, 0).addChange(change).build()
+
+        callOnTransitionReady(transitionInfo)
+
+        verifyTaskAddedAndEnterLogging(
+            EnterReason.OVERVIEW,
+            DEFAULT_TASK_UPDATE.copy(visibleTaskCount = 1),
+        )
+    }
+
+    @Test
+    fun transitOpen_previousTransitionExitToOverview_logTaskAddedAndEnterReasonOverview() {
+        // previous exit to overview transition
+        // add a freeform task
+        val previousTaskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM)
+        transitionObserver.addTaskInfosToCachedMap(previousTaskInfo)
+        transitionObserver.isSessionActive = true
+        val previousTransitionInfo =
+            TransitionInfoBuilder(TRANSIT_TO_FRONT, TRANSIT_FLAG_IS_RECENTS)
+                .addChange(createChange(TRANSIT_TO_BACK, previousTaskInfo))
+                .build()
+
+        callOnTransitionReady(previousTransitionInfo)
+
+        verifyTaskRemovedAndExitLogging(ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE)
+
+        // Enter desktop mode from cancelled recents has no transition. Enter is detected on the
+        // next transition involving freeform windows
+
+        // TRANSIT_OPEN
+        val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FREEFORM))
+        val transitionInfo = TransitionInfoBuilder(TRANSIT_OPEN, 0).addChange(change).build()
+
+        callOnTransitionReady(transitionInfo)
+
+        verifyTaskAddedAndEnterLogging(
+            EnterReason.OVERVIEW,
+            DEFAULT_TASK_UPDATE.copy(visibleTaskCount = 1),
+        )
+    }
+
+    @Test
+    @Suppress("ktlint:standard:max-line-length")
+    fun transitEnterDesktopFromAppFromOverview_previousTransitionExitToOverview_logTaskAddedAndEnterReasonAppFromOverview() {
+        // Tests for AppFromOverview precedence in compared to cancelled Overview
+
+        // previous exit to overview transition
+        // add a freeform task
+        val previousTaskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM)
+        transitionObserver.addTaskInfosToCachedMap(previousTaskInfo)
+        transitionObserver.isSessionActive = true
+        val previousTransitionInfo =
+            TransitionInfoBuilder(TRANSIT_TO_FRONT, TRANSIT_FLAG_IS_RECENTS)
+                .addChange(createChange(TRANSIT_TO_BACK, previousTaskInfo))
+                .build()
+
+        callOnTransitionReady(previousTransitionInfo)
+
+        verifyTaskRemovedAndExitLogging(ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE)
+
+        // TRANSIT_ENTER_DESKTOP_FROM_APP_FROM_OVERVIEW
+        val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FREEFORM))
+        val transitionInfo =
+            TransitionInfoBuilder(TRANSIT_ENTER_DESKTOP_FROM_APP_FROM_OVERVIEW, 0)
+                .addChange(change)
+                .build()
+
+        callOnTransitionReady(transitionInfo)
+
+        verifyTaskAddedAndEnterLogging(
+            EnterReason.APP_FROM_OVERVIEW,
+            DEFAULT_TASK_UPDATE.copy(visibleTaskCount = 1),
+        )
+    }
+
+    @Test
+    fun transitEnterDesktopFromUnknown_logTaskAddedAndEnterReasonUnknown() {
+        val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FREEFORM))
+        val transitionInfo =
+            TransitionInfoBuilder(TRANSIT_ENTER_DESKTOP_FROM_UNKNOWN, 0).addChange(change).build()
+
+        callOnTransitionReady(transitionInfo)
+
+        verifyTaskAddedAndEnterLogging(
+            EnterReason.UNKNOWN_ENTER,
+            DEFAULT_TASK_UPDATE.copy(visibleTaskCount = 1),
+        )
+    }
+
+    @Test
+    fun transitWake_logTaskAddedAndEnterReasonScreenOn() {
+        val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FREEFORM))
+        val transitionInfo = TransitionInfoBuilder(TRANSIT_WAKE, 0).addChange(change).build()
+
+        callOnTransitionReady(transitionInfo)
+
+        verifyTaskAddedAndEnterLogging(
+            EnterReason.SCREEN_ON,
+            DEFAULT_TASK_UPDATE.copy(visibleTaskCount = 1),
+        )
+    }
+
+    @Test
+    fun transitBack_previousExitReasonScreenOff_logTaskAddedAndEnterReasonScreenOn() {
+        val freeformTask = createTaskInfo(WINDOWING_MODE_FREEFORM)
+        // Previous Exit reason recorded as Screen Off
+        transitionObserver.addTaskInfosToCachedMap(freeformTask)
+        transitionObserver.isSessionActive = true
+        callOnTransitionReady(TransitionInfoBuilder(TRANSIT_SLEEP).build())
+        verifyTaskRemovedAndExitLogging(ExitReason.SCREEN_OFF, DEFAULT_TASK_UPDATE)
+        // Enter desktop through back transition, this happens when user enters after dismissing
+        // keyguard
+        val change = createChange(TRANSIT_TO_FRONT, freeformTask)
+        val transitionInfo = TransitionInfoBuilder(TRANSIT_TO_BACK, 0).addChange(change).build()
+
+        callOnTransitionReady(transitionInfo)
+
+        verifyTaskAddedAndEnterLogging(
+            EnterReason.SCREEN_ON,
+            DEFAULT_TASK_UPDATE.copy(visibleTaskCount = 1),
+        )
+    }
+
+    @Test
+    fun transitEndDragToDesktop_previousExitReasonScreenOff_logTaskAddedAndEnterReasonAppDrag() {
+        val freeformTask = createTaskInfo(WINDOWING_MODE_FREEFORM)
+        // Previous Exit reason recorded as Screen Off
+        transitionObserver.addTaskInfosToCachedMap(freeformTask)
+        transitionObserver.isSessionActive = true
+        callOnTransitionReady(TransitionInfoBuilder(TRANSIT_SLEEP).build())
+        verifyTaskRemovedAndExitLogging(ExitReason.SCREEN_OFF, DEFAULT_TASK_UPDATE)
+
+        // Enter desktop through app handle drag. This represents cases where instead of moving to
+        // desktop right after turning the screen on, we move to fullscreen then move another task
+        // to desktop
+        val transitionInfo =
+            TransitionInfoBuilder(Transitions.TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP, 0)
+                .addChange(createChange(TRANSIT_TO_FRONT, freeformTask))
+                .build()
+        callOnTransitionReady(transitionInfo)
+
+        verifyTaskAddedAndEnterLogging(
+            EnterReason.APP_HANDLE_DRAG,
+            DEFAULT_TASK_UPDATE.copy(visibleTaskCount = 1),
+        )
+    }
+
+    @Test
+    fun transitSleep_logTaskRemovedAndExitReasonScreenOff() {
+        // add a freeform task
+        transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM))
+        transitionObserver.isSessionActive = true
+
+        val transitionInfo = TransitionInfoBuilder(TRANSIT_SLEEP).build()
+        callOnTransitionReady(transitionInfo)
+
+        verifyTaskRemovedAndExitLogging(ExitReason.SCREEN_OFF, DEFAULT_TASK_UPDATE)
+    }
+
+    @Test
+    fun transitExitDesktopTaskDrag_logTaskRemovedAndExitReasonDragToExit() {
+        // add a freeform task
+        transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM))
+        transitionObserver.isSessionActive = true
+
+        // window mode changing from FREEFORM to FULLSCREEN
+        val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FULLSCREEN))
+        val transitionInfo =
+            TransitionInfoBuilder(TRANSIT_EXIT_DESKTOP_MODE_TASK_DRAG).addChange(change).build()
+        callOnTransitionReady(transitionInfo)
+
+        verifyTaskRemovedAndExitLogging(ExitReason.DRAG_TO_EXIT, DEFAULT_TASK_UPDATE)
+    }
+
+    @Test
+    fun transitExitDesktopAppHandleButton_logTaskRemovedAndExitReasonButton() {
+        // add a freeform task
+        transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM))
+        transitionObserver.isSessionActive = true
+
+        // window mode changing from FREEFORM to FULLSCREEN
+        val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FULLSCREEN))
+        val transitionInfo =
+            TransitionInfoBuilder(TRANSIT_EXIT_DESKTOP_MODE_HANDLE_MENU_BUTTON)
+                .addChange(change)
+                .build()
+        callOnTransitionReady(transitionInfo)
+
+        verifyTaskRemovedAndExitLogging(ExitReason.APP_HANDLE_MENU_BUTTON_EXIT, DEFAULT_TASK_UPDATE)
+    }
+
+    @Test
+    fun transitExitDesktopUsingKeyboard_logTaskRemovedAndExitReasonKeyboard() {
+        // add a freeform task
+        transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM))
+        transitionObserver.isSessionActive = true
+
+        // window mode changing from FREEFORM to FULLSCREEN
+        val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FULLSCREEN))
+        val transitionInfo =
+            TransitionInfoBuilder(TRANSIT_EXIT_DESKTOP_MODE_KEYBOARD_SHORTCUT)
+                .addChange(change)
+                .build()
+        callOnTransitionReady(transitionInfo)
+
+        verifyTaskRemovedAndExitLogging(ExitReason.KEYBOARD_SHORTCUT_EXIT, DEFAULT_TASK_UPDATE)
+    }
+
+    @Test
+    fun transitExitDesktopUnknown_logTaskRemovedAndExitReasonUnknown() {
+        // add a freeform task
+        transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM))
+        transitionObserver.isSessionActive = true
+
+        // window mode changing from FREEFORM to FULLSCREEN
+        val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FULLSCREEN))
+        val transitionInfo =
+            TransitionInfoBuilder(TRANSIT_EXIT_DESKTOP_MODE_UNKNOWN).addChange(change).build()
+        callOnTransitionReady(transitionInfo)
+
+        verifyTaskRemovedAndExitLogging(ExitReason.UNKNOWN_EXIT, DEFAULT_TASK_UPDATE)
+    }
+
+    @Test
+    fun transitToFrontWithFlagRecents_logTaskRemovedAndExitReasonOverview() {
+        // add a freeform task
+        transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM))
+        transitionObserver.isSessionActive = true
+
+        // recents transition
+        val change = createChange(TRANSIT_TO_BACK, createTaskInfo(WINDOWING_MODE_FREEFORM))
+        val transitionInfo =
+            TransitionInfoBuilder(TRANSIT_TO_FRONT, TRANSIT_FLAG_IS_RECENTS)
+                .addChange(change)
+                .build()
+        callOnTransitionReady(transitionInfo)
+
+        verifyTaskRemovedAndExitLogging(ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE)
+    }
+
+    @Test
+    fun transitClose_logTaskRemovedAndExitReasonTaskFinished() {
+        // add a freeform task
+        transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM))
+        transitionObserver.isSessionActive = true
+
+        // task closing
+        val change = createChange(TRANSIT_CLOSE, createTaskInfo(WINDOWING_MODE_FULLSCREEN))
+        val transitionInfo = TransitionInfoBuilder(TRANSIT_CLOSE).addChange(change).build()
+        callOnTransitionReady(transitionInfo)
+
+        verifyTaskRemovedAndExitLogging(ExitReason.TASK_FINISHED, DEFAULT_TASK_UPDATE)
+    }
+
+    @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)
+        transitionObserver.addTaskInfosToCachedMap(taskInfo)
+        transitionObserver.isSessionActive = true
+
+        // recents transition sent freeform window to back
+        val change = createChange(TRANSIT_TO_BACK, taskInfo)
+        val transitionInfo1 =
+            TransitionInfoBuilder(TRANSIT_TO_FRONT, TRANSIT_FLAG_IS_RECENTS)
+                .addChange(change)
+                .build()
+        callOnTransitionReady(transitionInfo1)
+
+        verifyTaskRemovedAndExitLogging(ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE)
+
+        val transitionInfo2 = TransitionInfoBuilder(TRANSIT_NONE).build()
+        callOnTransitionReady(transitionInfo2)
+
+        verifyTaskAddedAndEnterLogging(
+            EnterReason.OVERVIEW,
+            DEFAULT_TASK_UPDATE.copy(visibleTaskCount = 1),
+        )
+    }
+
+    @Test
+    fun sessionAlreadyStarted_newFreeformTaskAdded_logsTaskAdded() {
+        // add an existing freeform task
+        transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM))
+        transitionObserver.isSessionActive = true
+
+        // new freeform task added
+        val change = createChange(TRANSIT_OPEN, createTaskInfo(WINDOWING_MODE_FREEFORM, id = 2))
+        val transitionInfo = TransitionInfoBuilder(TRANSIT_OPEN, 0).addChange(change).build()
+        callOnTransitionReady(transitionInfo)
+
+        verify(desktopModeEventLogger, times(1))
+            .logTaskAdded(eq(DEFAULT_TASK_UPDATE.copy(instanceId = 2, visibleTaskCount = 2)))
+        verify(desktopModeEventLogger, never()).logSessionEnter(any())
+    }
+
+    @Test
+    fun sessionAlreadyStarted_taskPositionChanged_logsTaskUpdate() {
+        // add an existing freeform task
+        val taskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM)
+        transitionObserver.addTaskInfosToCachedMap(taskInfo)
+        transitionObserver.isSessionActive = true
+
+        // task position changed
+        val newTaskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM, taskX = DEFAULT_TASK_X + 100)
+        val transitionInfo =
+            TransitionInfoBuilder(TRANSIT_CHANGE, 0)
+                .addChange(createChange(TRANSIT_CHANGE, newTaskInfo))
+                .build()
+        callOnTransitionReady(transitionInfo)
+
+        verify(desktopModeEventLogger, times(1))
+            .logTaskInfoChanged(
+                eq(DEFAULT_TASK_UPDATE.copy(taskX = DEFAULT_TASK_X + 100, visibleTaskCount = 1))
+            )
+        verifyZeroInteractions(desktopModeEventLogger)
+    }
+
+    @Test
+    fun sessionAlreadyStarted_taskResized_logsTaskUpdate() {
+        // add an existing freeform task
+        val taskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM)
+        transitionObserver.addTaskInfosToCachedMap(taskInfo)
+        transitionObserver.isSessionActive = true
+
+        // task resized
+        val newTaskInfo =
+            createTaskInfo(
+                WINDOWING_MODE_FREEFORM,
+                taskWidth = DEFAULT_TASK_WIDTH + 100,
+                taskHeight = DEFAULT_TASK_HEIGHT - 100,
+            )
+        val transitionInfo =
+            TransitionInfoBuilder(TRANSIT_CHANGE, 0)
+                .addChange(createChange(TRANSIT_CHANGE, newTaskInfo))
+                .build()
+        callOnTransitionReady(transitionInfo)
+
+        verify(desktopModeEventLogger, times(1))
+            .logTaskInfoChanged(
+                eq(
+                    DEFAULT_TASK_UPDATE.copy(
+                        taskWidth = DEFAULT_TASK_WIDTH + 100,
+                        taskHeight = DEFAULT_TASK_HEIGHT - 100,
+                        visibleTaskCount = 1,
+                    )
+                )
+            )
+        verifyZeroInteractions(desktopModeEventLogger)
+    }
+
+    @Test
+    fun sessionAlreadyStarted_multipleTasksUpdated_logsTaskUpdateForCorrectTask() {
+        // add 2 existing freeform task
+        val taskInfo1 = createTaskInfo(WINDOWING_MODE_FREEFORM)
+        val taskInfo2 = createTaskInfo(WINDOWING_MODE_FREEFORM, id = 2)
+        transitionObserver.addTaskInfosToCachedMap(taskInfo1)
+        transitionObserver.addTaskInfosToCachedMap(taskInfo2)
+        transitionObserver.isSessionActive = true
+
+        // task 1 position update
+        val newTaskInfo1 = createTaskInfo(WINDOWING_MODE_FREEFORM, taskX = DEFAULT_TASK_X + 100)
+        val transitionInfo1 =
+            TransitionInfoBuilder(TRANSIT_CHANGE, 0)
+                .addChange(createChange(TRANSIT_CHANGE, newTaskInfo1))
+                .build()
+        callOnTransitionReady(transitionInfo1)
+
+        verify(desktopModeEventLogger, times(1))
+            .logTaskInfoChanged(
+                eq(DEFAULT_TASK_UPDATE.copy(taskX = DEFAULT_TASK_X + 100, visibleTaskCount = 2))
+            )
+        verifyZeroInteractions(desktopModeEventLogger)
+
+        // task 2 resize
+        val newTaskInfo2 =
+            createTaskInfo(
+                WINDOWING_MODE_FREEFORM,
+                id = 2,
+                taskWidth = DEFAULT_TASK_WIDTH + 100,
+                taskHeight = DEFAULT_TASK_HEIGHT - 100,
+            )
+        val transitionInfo2 =
+            TransitionInfoBuilder(TRANSIT_CHANGE, 0)
+                .addChange(createChange(TRANSIT_CHANGE, newTaskInfo2))
+                .build()
+
+        callOnTransitionReady(transitionInfo2)
+
+        verify(desktopModeEventLogger, times(1))
+            .logTaskInfoChanged(
+                eq(
+                    DEFAULT_TASK_UPDATE.copy(
+                        instanceId = 2,
+                        taskWidth = DEFAULT_TASK_WIDTH + 100,
+                        taskHeight = DEFAULT_TASK_HEIGHT - 100,
+                        visibleTaskCount = 2,
+                    )
+                )
+            )
+        verifyZeroInteractions(desktopModeEventLogger)
+    }
+
+    @Test
+    fun sessionAlreadyStarted_freeformTaskRemoved_logsTaskRemoved() {
+        // add two existing freeform tasks
+        transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM))
+        transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM, id = 2))
+        transitionObserver.isSessionActive = true
+
+        // new freeform task closed
+        val change = createChange(TRANSIT_CLOSE, createTaskInfo(WINDOWING_MODE_FREEFORM, id = 2))
+        val transitionInfo = TransitionInfoBuilder(TRANSIT_CLOSE, 0).addChange(change).build()
+        callOnTransitionReady(transitionInfo)
+
+        verify(desktopModeEventLogger, times(1))
+            .logTaskRemoved(eq(DEFAULT_TASK_UPDATE.copy(instanceId = 2, visibleTaskCount = 1)))
+        verify(desktopModeEventLogger, never()).logSessionExit(any())
+    }
+
+    /** Simulate calling the onTransitionReady() method */
+    private fun callOnTransitionReady(transitionInfo: TransitionInfo) {
+        val transition = mock<IBinder>()
+        val startT = mock<SurfaceControl.Transaction>()
+        val finishT = mock<SurfaceControl.Transaction>()
+
+        transitionObserver.onTransitionReady(transition, transitionInfo, startT, finishT)
+    }
+
+    private fun verifyTaskAddedAndEnterLogging(enterReason: EnterReason, taskUpdate: TaskUpdate) {
+        assertTrue(transitionObserver.isSessionActive)
+        verify(desktopModeEventLogger, times(1)).logSessionEnter(eq(enterReason))
+        verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(taskUpdate))
+        ExtendedMockito.verify {
+            Trace.setCounter(
+                eq(Trace.TRACE_TAG_WINDOW_MANAGER),
+                eq(DesktopModeLoggerTransitionObserver.VISIBLE_TASKS_COUNTER_NAME),
+                eq(taskUpdate.visibleTaskCount.toLong()),
+            )
+        }
+        ExtendedMockito.verify {
+            SystemProperties.set(
+                eq(DesktopModeLoggerTransitionObserver.VISIBLE_TASKS_COUNTER_SYSTEM_PROPERTY),
+                eq(taskUpdate.visibleTaskCount.toString()),
+            )
+        }
+        verifyZeroInteractions(desktopModeEventLogger)
+    }
+
+    private fun verifyTaskRemovedAndExitLogging(exitReason: ExitReason, taskUpdate: TaskUpdate) {
+        assertFalse(transitionObserver.isSessionActive)
+        verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(taskUpdate))
+        verify(desktopModeEventLogger, times(1)).logSessionExit(eq(exitReason))
+        verifyZeroInteractions(desktopModeEventLogger)
+    }
+
+    private companion object {
+        const val DEFAULT_TASK_ID = 1
+        const val DEFAULT_TASK_UID = 2
+        const val DEFAULT_TASK_HEIGHT = 100
+        const val DEFAULT_TASK_WIDTH = 200
+        const val DEFAULT_TASK_X = 30
+        const val DEFAULT_TASK_Y = 70
+        const val DEFAULT_VISIBLE_TASK_COUNT = 0
+        val DEFAULT_TASK_UPDATE =
+            TaskUpdate(
+                DEFAULT_TASK_ID,
+                DEFAULT_TASK_UID,
+                DEFAULT_TASK_HEIGHT,
+                DEFAULT_TASK_WIDTH,
+                DEFAULT_TASK_X,
+                DEFAULT_TASK_Y,
+                visibleTaskCount = DEFAULT_VISIBLE_TASK_COUNT,
+            )
+
+        fun createTaskInfo(
+            windowMode: Int,
+            id: Int = DEFAULT_TASK_ID,
+            uid: Int = DEFAULT_TASK_UID,
+            taskHeight: Int = DEFAULT_TASK_HEIGHT,
+            taskWidth: Int = DEFAULT_TASK_WIDTH,
+            taskX: Int = DEFAULT_TASK_X,
+            taskY: Int = DEFAULT_TASK_Y,
+        ) =
+            ActivityManager.RunningTaskInfo().apply {
+                taskId = id
+                effectiveUid = uid
+                configuration.windowConfiguration.apply {
+                    windowingMode = windowMode
+                    positionInParent = Point(taskX, taskY)
+                    bounds.set(Rect(taskX, taskY, taskX + taskWidth, taskY + taskHeight))
+                }
+            }
+
+        fun createChange(mode: Int, taskInfo: ActivityManager.RunningTaskInfo): Change {
+            val change =
+                Change(WindowContainerToken(mock<IWindowContainerToken>()), mock<SurfaceControl>())
+            change.mode = mode
+            change.taskInfo = taskInfo
+            return change
+        }
+    }
 }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTransitionTypesTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTransitionTypesTest.kt
index db4e93d..f6eed5d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTransitionTypesTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTransitionTypesTest.kt
@@ -18,18 +18,18 @@
 
 import android.testing.AndroidTestingRunner
 import androidx.test.filters.SmallTest
-import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_HANDLE_MENU_BUTTON
-import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_KEYBOARD_SHORTCUT
-import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_TASK_DRAG
-import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_UNKNOWN
 import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_ENTER_DESKTOP_FROM_APP_FROM_OVERVIEW
 import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_ENTER_DESKTOP_FROM_APP_HANDLE_MENU_BUTTON
 import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_ENTER_DESKTOP_FROM_KEYBOARD_SHORTCUT
 import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_ENTER_DESKTOP_FROM_UNKNOWN
+import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_HANDLE_MENU_BUTTON
+import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_KEYBOARD_SHORTCUT
+import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_TASK_DRAG
+import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_UNKNOWN
 import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.getEnterTransitionType
 import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.getExitTransitionType
-import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource.APP_HANDLE_MENU_BUTTON
 import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource.APP_FROM_OVERVIEW
+import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource.APP_HANDLE_MENU_BUTTON
 import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource.KEYBOARD_SHORTCUT
 import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource.TASK_DRAG
 import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource.UNKNOWN
@@ -53,8 +53,7 @@
             .isEqualTo(TRANSIT_ENTER_DESKTOP_FROM_APP_HANDLE_MENU_BUTTON)
         assertThat(APP_FROM_OVERVIEW.getEnterTransitionType())
             .isEqualTo(TRANSIT_ENTER_DESKTOP_FROM_APP_FROM_OVERVIEW)
-        assertThat(TASK_DRAG.getEnterTransitionType())
-            .isEqualTo(TRANSIT_ENTER_DESKTOP_FROM_UNKNOWN)
+        assertThat(TASK_DRAG.getEnterTransitionType()).isEqualTo(TRANSIT_ENTER_DESKTOP_FROM_UNKNOWN)
         assertThat(KEYBOARD_SHORTCUT.getEnterTransitionType())
             .isEqualTo(TRANSIT_ENTER_DESKTOP_FROM_KEYBOARD_SHORTCUT)
     }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeUiEventLoggerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeUiEventLoggerTest.kt
index 94698e2..72b1fd9 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeUiEventLoggerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeUiEventLoggerTest.kt
@@ -16,7 +16,6 @@
 
 package com.android.wm.shell.desktopmode
 
-
 import android.content.ComponentName
 import android.content.pm.ApplicationInfo
 import android.content.pm.PackageManager
@@ -67,8 +66,7 @@
 
     @Test
     fun log_eventLogged() {
-        val event =
-            DESKTOP_WINDOW_EDGE_DRAG_RESIZE
+        val event = DESKTOP_WINDOW_EDGE_DRAG_RESIZE
         logger.log(UID, PACKAGE_NAME, event)
         assertThat(uiEventLoggerFake.numLogs()).isEqualTo(1)
         assertThat(uiEventLoggerFake.eventId(0)).isEqualTo(event.id)
@@ -97,8 +95,7 @@
 
     @Test
     fun logWithInstanceId_eventLogged() {
-        val event =
-            DESKTOP_WINDOW_EDGE_DRAG_RESIZE
+        val event = DESKTOP_WINDOW_EDGE_DRAG_RESIZE
         logger.logWithInstanceId(INSTANCE_ID, UID, PACKAGE_NAME, event)
         assertThat(uiEventLoggerFake.numLogs()).isEqualTo(1)
         assertThat(uiEventLoggerFake.eventId(0)).isEqualTo(event.id)
@@ -109,12 +106,12 @@
 
     @Test
     fun logWithTaskInfo_eventLogged() {
-        val event =
-            DESKTOP_WINDOW_EDGE_DRAG_RESIZE
-        val taskInfo = TestRunningTaskInfoBuilder()
-            .setUserId(USER_ID)
-            .setBaseActivity(ComponentName(PACKAGE_NAME, "test"))
-            .build()
+        val event = DESKTOP_WINDOW_EDGE_DRAG_RESIZE
+        val taskInfo =
+            TestRunningTaskInfoBuilder()
+                .setUserId(USER_ID)
+                .setBaseActivity(ComponentName(PACKAGE_NAME, "test"))
+                .build()
         whenever(mockPackageManager.getApplicationInfoAsUser(PACKAGE_NAME, /* flags= */ 0, USER_ID))
             .thenReturn(ApplicationInfo().apply { uid = UID })
         logger.log(taskInfo, event)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicatorTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicatorTest.kt
index 935e6d0..e46d2c7 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicatorTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicatorTest.kt
@@ -66,74 +66,109 @@
     fun testFullscreenRegionCalculation() {
         createVisualIndicator(DesktopModeVisualIndicator.DragStartState.FROM_FULLSCREEN)
         var testRegion = visualIndicator.calculateFullscreenRegion(displayLayout, CAPTION_HEIGHT)
-        assertThat(testRegion.bounds).isEqualTo(Rect(0, Short.MIN_VALUE.toInt(), 2400,
-            2 * STABLE_INSETS.top))
+        assertThat(testRegion.bounds)
+            .isEqualTo(Rect(0, Short.MIN_VALUE.toInt(), 2400, 2 * STABLE_INSETS.top))
 
         createVisualIndicator(DesktopModeVisualIndicator.DragStartState.FROM_FREEFORM)
         testRegion = visualIndicator.calculateFullscreenRegion(displayLayout, CAPTION_HEIGHT)
         val transitionHeight = SystemBarUtils.getStatusBarHeight(context)
-        val toFullscreenScale = mContext.resources.getFloat(
-            R.dimen.desktop_mode_fullscreen_region_scale
-        )
+        val toFullscreenScale =
+            mContext.resources.getFloat(R.dimen.desktop_mode_fullscreen_region_scale)
         val toFullscreenWidth = displayLayout.width() * toFullscreenScale
-        assertThat(testRegion.bounds).isEqualTo(Rect(
-            (DISPLAY_BOUNDS.width() / 2f - toFullscreenWidth / 2f).toInt(),
-            Short.MIN_VALUE.toInt(),
-            (DISPLAY_BOUNDS.width() / 2f + toFullscreenWidth / 2f).toInt(),
-            transitionHeight))
+        assertThat(testRegion.bounds)
+            .isEqualTo(
+                Rect(
+                    (DISPLAY_BOUNDS.width() / 2f - toFullscreenWidth / 2f).toInt(),
+                    Short.MIN_VALUE.toInt(),
+                    (DISPLAY_BOUNDS.width() / 2f + toFullscreenWidth / 2f).toInt(),
+                    transitionHeight,
+                )
+            )
 
         createVisualIndicator(DesktopModeVisualIndicator.DragStartState.FROM_SPLIT)
         testRegion = visualIndicator.calculateFullscreenRegion(displayLayout, CAPTION_HEIGHT)
-        assertThat(testRegion.bounds).isEqualTo(Rect(0, Short.MIN_VALUE.toInt(), 2400,
-            2 * STABLE_INSETS.top))
+        assertThat(testRegion.bounds)
+            .isEqualTo(Rect(0, Short.MIN_VALUE.toInt(), 2400, 2 * STABLE_INSETS.top))
 
         createVisualIndicator(DesktopModeVisualIndicator.DragStartState.DRAGGED_INTENT)
         testRegion = visualIndicator.calculateFullscreenRegion(displayLayout, CAPTION_HEIGHT)
-        assertThat(testRegion.bounds).isEqualTo(Rect(0, Short.MIN_VALUE.toInt(), 2400,
-            transitionHeight))
+        assertThat(testRegion.bounds)
+            .isEqualTo(Rect(0, Short.MIN_VALUE.toInt(), 2400, transitionHeight))
     }
 
     @Test
     fun testSplitLeftRegionCalculation() {
-        val transitionHeight = context.resources.getDimensionPixelSize(
-            R.dimen.desktop_mode_split_from_desktop_height)
+        val transitionHeight =
+            context.resources.getDimensionPixelSize(R.dimen.desktop_mode_split_from_desktop_height)
         createVisualIndicator(DesktopModeVisualIndicator.DragStartState.FROM_FULLSCREEN)
-        var testRegion = visualIndicator.calculateSplitLeftRegion(displayLayout,
-            TRANSITION_AREA_WIDTH, CAPTION_HEIGHT)
+        var testRegion =
+            visualIndicator.calculateSplitLeftRegion(
+                displayLayout,
+                TRANSITION_AREA_WIDTH,
+                CAPTION_HEIGHT,
+            )
         assertThat(testRegion.bounds).isEqualTo(Rect(0, -50, 32, 1600))
         createVisualIndicator(DesktopModeVisualIndicator.DragStartState.FROM_FREEFORM)
-        testRegion = visualIndicator.calculateSplitLeftRegion(displayLayout,
-            TRANSITION_AREA_WIDTH, CAPTION_HEIGHT)
+        testRegion =
+            visualIndicator.calculateSplitLeftRegion(
+                displayLayout,
+                TRANSITION_AREA_WIDTH,
+                CAPTION_HEIGHT,
+            )
         assertThat(testRegion.bounds).isEqualTo(Rect(0, transitionHeight, 32, 1600))
         createVisualIndicator(DesktopModeVisualIndicator.DragStartState.FROM_SPLIT)
-        testRegion = visualIndicator.calculateSplitLeftRegion(displayLayout,
-            TRANSITION_AREA_WIDTH, CAPTION_HEIGHT)
+        testRegion =
+            visualIndicator.calculateSplitLeftRegion(
+                displayLayout,
+                TRANSITION_AREA_WIDTH,
+                CAPTION_HEIGHT,
+            )
         assertThat(testRegion.bounds).isEqualTo(Rect(0, -50, 32, 1600))
         createVisualIndicator(DesktopModeVisualIndicator.DragStartState.DRAGGED_INTENT)
-        testRegion = visualIndicator.calculateSplitLeftRegion(displayLayout,
-            TRANSITION_AREA_WIDTH, CAPTION_HEIGHT)
+        testRegion =
+            visualIndicator.calculateSplitLeftRegion(
+                displayLayout,
+                TRANSITION_AREA_WIDTH,
+                CAPTION_HEIGHT,
+            )
         assertThat(testRegion.bounds).isEqualTo(Rect(0, -50, 32, 1600))
     }
 
     @Test
     fun testSplitRightRegionCalculation() {
-        val transitionHeight = context.resources.getDimensionPixelSize(
-            R.dimen.desktop_mode_split_from_desktop_height)
+        val transitionHeight =
+            context.resources.getDimensionPixelSize(R.dimen.desktop_mode_split_from_desktop_height)
         createVisualIndicator(DesktopModeVisualIndicator.DragStartState.FROM_FULLSCREEN)
-        var testRegion = visualIndicator.calculateSplitRightRegion(displayLayout,
-            TRANSITION_AREA_WIDTH, CAPTION_HEIGHT)
+        var testRegion =
+            visualIndicator.calculateSplitRightRegion(
+                displayLayout,
+                TRANSITION_AREA_WIDTH,
+                CAPTION_HEIGHT,
+            )
         assertThat(testRegion.bounds).isEqualTo(Rect(2368, -50, 2400, 1600))
         createVisualIndicator(DesktopModeVisualIndicator.DragStartState.FROM_FREEFORM)
-        testRegion = visualIndicator.calculateSplitRightRegion(displayLayout,
-            TRANSITION_AREA_WIDTH, CAPTION_HEIGHT)
+        testRegion =
+            visualIndicator.calculateSplitRightRegion(
+                displayLayout,
+                TRANSITION_AREA_WIDTH,
+                CAPTION_HEIGHT,
+            )
         assertThat(testRegion.bounds).isEqualTo(Rect(2368, transitionHeight, 2400, 1600))
         createVisualIndicator(DesktopModeVisualIndicator.DragStartState.FROM_SPLIT)
-        testRegion = visualIndicator.calculateSplitRightRegion(displayLayout,
-            TRANSITION_AREA_WIDTH, CAPTION_HEIGHT)
+        testRegion =
+            visualIndicator.calculateSplitRightRegion(
+                displayLayout,
+                TRANSITION_AREA_WIDTH,
+                CAPTION_HEIGHT,
+            )
         assertThat(testRegion.bounds).isEqualTo(Rect(2368, -50, 2400, 1600))
         createVisualIndicator(DesktopModeVisualIndicator.DragStartState.DRAGGED_INTENT)
-        testRegion = visualIndicator.calculateSplitRightRegion(displayLayout,
-            TRANSITION_AREA_WIDTH, CAPTION_HEIGHT)
+        testRegion =
+            visualIndicator.calculateSplitRightRegion(
+                displayLayout,
+                TRANSITION_AREA_WIDTH,
+                CAPTION_HEIGHT,
+            )
         assertThat(testRegion.bounds).isEqualTo(Rect(2368, -50, 2400, 1600))
     }
 
@@ -141,10 +176,12 @@
     fun testDefaultIndicators() {
         createVisualIndicator(DesktopModeVisualIndicator.DragStartState.FROM_FULLSCREEN)
         var result = visualIndicator.updateIndicatorType(PointF(-10000f, 500f))
-        assertThat(result).isEqualTo(DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_LEFT_INDICATOR)
+        assertThat(result)
+            .isEqualTo(DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_LEFT_INDICATOR)
         createVisualIndicator(DesktopModeVisualIndicator.DragStartState.FROM_SPLIT)
         result = visualIndicator.updateIndicatorType(PointF(10000f, 500f))
-        assertThat(result).isEqualTo(DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_RIGHT_INDICATOR)
+        assertThat(result)
+            .isEqualTo(DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_RIGHT_INDICATOR)
         createVisualIndicator(DesktopModeVisualIndicator.DragStartState.DRAGGED_INTENT)
         result = visualIndicator.updateIndicatorType(PointF(500f, 10000f))
         assertThat(result).isEqualTo(DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR)
@@ -154,8 +191,16 @@
     }
 
     private fun createVisualIndicator(dragStartState: DesktopModeVisualIndicator.DragStartState) {
-        visualIndicator = DesktopModeVisualIndicator(syncQueue, taskInfo, displayController,
-            context, taskSurface, taskDisplayAreaOrganizer, dragStartState)
+        visualIndicator =
+            DesktopModeVisualIndicator(
+                syncQueue,
+                taskInfo,
+                displayController,
+                context,
+                taskSurface,
+                taskDisplayAreaOrganizer,
+                dragStartState,
+            )
     }
 
     companion object {
@@ -163,11 +208,12 @@
         private const val CAPTION_HEIGHT = 50
         private val DISPLAY_BOUNDS = Rect(0, 0, 2400, 1600)
         private const val NAVBAR_HEIGHT = 50
-        private val STABLE_INSETS = Rect(
-            DISPLAY_BOUNDS.left,
-            DISPLAY_BOUNDS.top + CAPTION_HEIGHT,
-            DISPLAY_BOUNDS.right,
-            DISPLAY_BOUNDS.bottom - NAVBAR_HEIGHT
-        )
+        private val STABLE_INSETS =
+            Rect(
+                DISPLAY_BOUNDS.left,
+                DISPLAY_BOUNDS.top + CAPTION_HEIGHT,
+                DISPLAY_BOUNDS.right,
+                DISPLAY_BOUNDS.bottom - NAVBAR_HEIGHT,
+            )
     }
-}
\ No newline at end of file
+}
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 344140d..5629127 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
@@ -56,6 +56,11 @@
 import org.mockito.kotlin.verify
 import org.mockito.kotlin.whenever
 
+/**
+ * Tests for [@link DesktopRepository].
+ *
+ * Build/Install/Run: atest WMShellUnitTests:DesktopRepositoryTest
+ */
 @SmallTest
 @RunWith(AndroidTestingRunner::class)
 @ExperimentalCoroutinesApi
@@ -76,15 +81,9 @@
         datastoreScope = CoroutineScope(Dispatchers.Unconfined + SupervisorJob())
         shellInit = spy(ShellInit(testExecutor))
 
-        repo =
-            DesktopRepository(
-                persistentRepository,
-                datastoreScope,
-                DEFAULT_USER_ID
-            )
-        whenever(runBlocking { persistentRepository.readDesktop(any(), any()) }).thenReturn(
-            Desktop.getDefaultInstance()
-        )
+        repo = DesktopRepository(persistentRepository, datastoreScope, DEFAULT_USER_ID)
+        whenever(runBlocking { persistentRepository.readDesktop(any(), any()) })
+            .thenReturn(Desktop.getDefaultInstance())
         shellInit.init()
     }
 
@@ -245,7 +244,7 @@
                         DEFAULT_DESKTOP_ID,
                         visibleTasks = ArraySet(arrayOf(1)),
                         minimizedTasks = ArraySet(),
-                        freeformTasksInZOrder = arrayListOf()
+                        freeformTasksInZOrder = arrayListOf(),
                     )
                 verify(persistentRepository)
                     .addOrUpdateDesktop(
@@ -253,7 +252,7 @@
                         DEFAULT_DESKTOP_ID,
                         visibleTasks = ArraySet(arrayOf(1, 2)),
                         minimizedTasks = ArraySet(),
-                        freeformTasksInZOrder = arrayListOf()
+                        freeformTasksInZOrder = arrayListOf(),
                     )
             }
         }
@@ -441,8 +440,8 @@
     }
 
     /**
-     * When a task vanishes, the displayId of the task is set to INVALID_DISPLAY.
-     * This tests that task is removed from the last parent display when it vanishes.
+     * When a task vanishes, the displayId of the task is set to INVALID_DISPLAY. This tests that
+     * task is removed from the last parent display when it vanishes.
      */
     @Test
     fun updateTask_removeVisibleTasksRemovesTaskWithInvalidDisplay() {
@@ -562,7 +561,7 @@
                         DEFAULT_DESKTOP_ID,
                         visibleTasks = ArraySet(),
                         minimizedTasks = ArraySet(),
-                        freeformTasksInZOrder = arrayListOf(5)
+                        freeformTasksInZOrder = arrayListOf(5),
                     )
                 verify(persistentRepository)
                     .addOrUpdateDesktop(
@@ -570,7 +569,7 @@
                         DEFAULT_DESKTOP_ID,
                         visibleTasks = ArraySet(arrayOf(5)),
                         minimizedTasks = ArraySet(),
-                        freeformTasksInZOrder = arrayListOf(6, 5)
+                        freeformTasksInZOrder = arrayListOf(6, 5),
                     )
                 verify(persistentRepository)
                     .addOrUpdateDesktop(
@@ -578,10 +577,10 @@
                         DEFAULT_DESKTOP_ID,
                         visibleTasks = ArraySet(arrayOf(5, 6)),
                         minimizedTasks = ArraySet(),
-                        freeformTasksInZOrder = arrayListOf(7, 6, 5)
+                        freeformTasksInZOrder = arrayListOf(7, 6, 5),
                     )
             }
-    }
+        }
 
     @Test
     fun addTask_alreadyExists_movesToTop() {
@@ -628,7 +627,7 @@
                         DEFAULT_DESKTOP_ID,
                         visibleTasks = ArraySet(),
                         minimizedTasks = ArraySet(),
-                        freeformTasksInZOrder = arrayListOf(5)
+                        freeformTasksInZOrder = arrayListOf(5),
                     )
                 verify(persistentRepository)
                     .addOrUpdateDesktop(
@@ -636,7 +635,7 @@
                         DEFAULT_DESKTOP_ID,
                         visibleTasks = ArraySet(arrayOf(5)),
                         minimizedTasks = ArraySet(),
-                        freeformTasksInZOrder = arrayListOf(6, 5)
+                        freeformTasksInZOrder = arrayListOf(6, 5),
                     )
                 verify(persistentRepository)
                     .addOrUpdateDesktop(
@@ -644,7 +643,7 @@
                         DEFAULT_DESKTOP_ID,
                         visibleTasks = ArraySet(arrayOf(5, 6)),
                         minimizedTasks = ArraySet(),
-                        freeformTasksInZOrder = arrayListOf(7, 6, 5)
+                        freeformTasksInZOrder = arrayListOf(7, 6, 5),
                     )
                 verify(persistentRepository, times(2))
                     .addOrUpdateDesktop(
@@ -652,10 +651,10 @@
                         DEFAULT_DESKTOP_ID,
                         visibleTasks = ArraySet(arrayOf(5, 7)),
                         minimizedTasks = ArraySet(arrayOf(6)),
-                        freeformTasksInZOrder = arrayListOf(7, 6, 5)
+                        freeformTasksInZOrder = arrayListOf(7, 6, 5),
                     )
             }
-    }
+        }
 
     @Test
     fun addTask_taskIsUnminimized_noop() {
@@ -694,7 +693,7 @@
                     DEFAULT_DESKTOP_ID,
                     visibleTasks = ArraySet(),
                     minimizedTasks = ArraySet(),
-                    freeformTasksInZOrder = arrayListOf(1)
+                    freeformTasksInZOrder = arrayListOf(1),
                 )
             verify(persistentRepository)
                 .addOrUpdateDesktop(
@@ -702,7 +701,7 @@
                     DEFAULT_DESKTOP_ID,
                     visibleTasks = ArraySet(),
                     minimizedTasks = ArraySet(),
-                    freeformTasksInZOrder = ArrayList()
+                    freeformTasksInZOrder = ArrayList(),
                 )
         }
     }
@@ -731,7 +730,7 @@
                     DEFAULT_DESKTOP_ID,
                     visibleTasks = ArraySet(),
                     minimizedTasks = ArraySet(),
-                    freeformTasksInZOrder = arrayListOf(1)
+                    freeformTasksInZOrder = arrayListOf(1),
                 )
             verify(persistentRepository)
                 .addOrUpdateDesktop(
@@ -739,7 +738,7 @@
                     DEFAULT_DESKTOP_ID,
                     visibleTasks = ArraySet(),
                     minimizedTasks = ArraySet(),
-                    freeformTasksInZOrder = ArrayList()
+                    freeformTasksInZOrder = ArrayList(),
                 )
         }
     }
@@ -768,7 +767,7 @@
                     DEFAULT_DESKTOP_ID,
                     visibleTasks = ArraySet(),
                     minimizedTasks = ArraySet(),
-                    freeformTasksInZOrder = arrayListOf(1)
+                    freeformTasksInZOrder = arrayListOf(1),
                 )
             verify(persistentRepository, never())
                 .addOrUpdateDesktop(
@@ -776,7 +775,7 @@
                     DEFAULT_DESKTOP_ID,
                     visibleTasks = ArraySet(),
                     minimizedTasks = ArraySet(),
-                    freeformTasksInZOrder = ArrayList()
+                    freeformTasksInZOrder = ArrayList(),
                 )
         }
     }
@@ -928,7 +927,6 @@
         assertThat(repo.isMinimizedTask(taskId = 2)).isFalse()
     }
 
-
     @Test
     fun updateTask_minimizedTaskBecomesVisible_unminimizesTask() {
         repo.minimizeTask(displayId = 10, taskId = 2)
@@ -985,6 +983,22 @@
     }
 
     @Test
+    fun setTaskIdAsTopTransparentFullscreenTaskId_savesTaskId() {
+        repo.setTopTransparentFullscreenTaskId(displayId = DEFAULT_DISPLAY, taskId = 1)
+
+        assertThat(repo.getTopTransparentFullscreenTaskId(DEFAULT_DISPLAY)).isEqualTo(1)
+    }
+
+    @Test
+    fun clearTaskIdAsTopTransparentFullscreenTaskId_clearsTaskId() {
+        repo.setTopTransparentFullscreenTaskId(displayId = DEFAULT_DISPLAY, taskId = 1)
+
+        repo.clearTopTransparentFullscreenTaskId(DEFAULT_DISPLAY)
+
+        assertThat(repo.getTopTransparentFullscreenTaskId(DEFAULT_DISPLAY)).isNull()
+    }
+
+    @Test
     fun setTaskInFullImmersiveState_savedAsInImmersiveState() {
         assertThat(repo.isTaskInFullImmersiveState(taskId = 1)).isFalse()
 
@@ -1056,6 +1070,7 @@
     class TestListener : DesktopRepository.ActiveTasksListener {
         var activeChangesOnDefaultDisplay = 0
         var activeChangesOnSecondaryDisplay = 0
+
         override fun onActiveTasksChanged(displayId: Int) {
             when (displayId) {
                 DEFAULT_DISPLAY -> activeChangesOnDefaultDisplay++
@@ -1093,4 +1108,4 @@
         private const val DEFAULT_USER_ID = 1000
         private const val DEFAULT_DESKTOP_ID = 0
     }
-}
\ No newline at end of file
+}
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
index b4daa66..19ab911 100644
--- 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
@@ -22,7 +22,6 @@
 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.desktopmode.DesktopUserRepositories
 import com.android.wm.shell.ShellTestCase
 import com.android.wm.shell.desktopmode.DesktopTestHelpers.createFreeformTask
 import com.android.wm.shell.desktopmode.DesktopTestHelpers.createFullscreenTask
@@ -45,167 +44,144 @@
 @RunWith(AndroidTestingRunner::class)
 class DesktopTaskChangeListenerTest : ShellTestCase() {
 
-  @JvmField @Rule val setFlagsRule = SetFlagsRule()
+    @JvmField @Rule val setFlagsRule = SetFlagsRule()
 
-  private lateinit var desktopTaskChangeListener: DesktopTaskChangeListener
+    private lateinit var desktopTaskChangeListener: DesktopTaskChangeListener
 
-  private val desktopUserRepositories = mock<DesktopUserRepositories>()
-  private val desktopRepository = mock<DesktopRepository>()
+    private val desktopUserRepositories = mock<DesktopUserRepositories>()
+    private val desktopRepository = mock<DesktopRepository>()
 
-  @Before
-  fun setUp() {
-    desktopTaskChangeListener = DesktopTaskChangeListener(desktopUserRepositories)
+    @Before
+    fun setUp() {
+        desktopTaskChangeListener = DesktopTaskChangeListener(desktopUserRepositories)
 
-    whenever(desktopUserRepositories.current).thenReturn(desktopRepository)
-    whenever(desktopUserRepositories.getProfile(anyInt())).thenReturn(desktopRepository)
-  }
+        whenever(desktopUserRepositories.current).thenReturn(desktopRepository)
+        whenever(desktopUserRepositories.getProfile(anyInt())).thenReturn(desktopRepository)
+    }
 
-  @Test
-  fun onTaskOpening_fullscreenTask_notActiveDesktopTask_noop() {
-    val task = createFullscreenTask().apply { isVisible = true }
-    whenever(desktopUserRepositories.current.isActiveTask(task.taskId))
-        .thenReturn(false)
+    @Test
+    fun onTaskOpening_fullscreenTask_notActiveDesktopTask_noop() {
+        val task = createFullscreenTask().apply { isVisible = true }
+        whenever(desktopUserRepositories.current.isActiveTask(task.taskId)).thenReturn(false)
 
-    desktopTaskChangeListener.onTaskOpening(task)
+        desktopTaskChangeListener.onTaskOpening(task)
 
-    verify(desktopUserRepositories.current, never())
-        .addTask(task.displayId, task.taskId, task.isVisible)
-    verify(desktopUserRepositories.current, never())
-        .removeFreeformTask(task.displayId, task.taskId)
-  }
+        verify(desktopUserRepositories.current, never())
+            .addTask(task.displayId, task.taskId, task.isVisible)
+        verify(desktopUserRepositories.current, never())
+            .removeFreeformTask(task.displayId, task.taskId)
+    }
 
-  @Test
-  fun onTaskOpening_freeformTask_activeDesktopTask_removesTaskFromRepo() {
-    val task = createFullscreenTask().apply { isVisible = true }
-    whenever(desktopUserRepositories.current.isActiveTask(task.taskId))
-        .thenReturn(true)
+    @Test
+    fun onTaskOpening_freeformTask_activeDesktopTask_removesTaskFromRepo() {
+        val task = createFullscreenTask().apply { isVisible = true }
+        whenever(desktopUserRepositories.current.isActiveTask(task.taskId)).thenReturn(true)
 
-    desktopTaskChangeListener.onTaskOpening(task)
+        desktopTaskChangeListener.onTaskOpening(task)
 
-    verify(desktopUserRepositories.current).removeFreeformTask(task.displayId, task.taskId)
-  }
+        verify(desktopUserRepositories.current).removeFreeformTask(task.displayId, task.taskId)
+    }
 
-  @Test
-  fun onTaskOpening_freeformTask_visibleDesktopTask_addsTaskToRepository() {
-    val task = createFreeformTask().apply { isVisible = true }
-    whenever(desktopUserRepositories.current.isActiveTask(task.taskId))
-        .thenReturn(false)
+    @Test
+    fun onTaskOpening_freeformTask_visibleDesktopTask_addsTaskToRepository() {
+        val task = createFreeformTask().apply { isVisible = true }
+        whenever(desktopUserRepositories.current.isActiveTask(task.taskId)).thenReturn(false)
 
-    desktopTaskChangeListener.onTaskOpening(task)
+        desktopTaskChangeListener.onTaskOpening(task)
 
-    verify(desktopUserRepositories.current)
-        .addTask(task.displayId, task.taskId, task.isVisible)
-  }
+        verify(desktopUserRepositories.current).addTask(task.displayId, task.taskId, task.isVisible)
+    }
 
-  @Test
-  fun onTaskOpening_freeformTask_nonVisibleDesktopTask_addsTaskToRepository() {
-    val task = createFreeformTask().apply { isVisible = false }
-    whenever(desktopUserRepositories.current.isActiveTask(task.taskId))
-        .thenReturn(true)
+    @Test
+    fun onTaskOpening_freeformTask_nonVisibleDesktopTask_addsTaskToRepository() {
+        val task = createFreeformTask().apply { isVisible = false }
+        whenever(desktopUserRepositories.current.isActiveTask(task.taskId)).thenReturn(true)
 
-    desktopTaskChangeListener.onTaskOpening(task)
+        desktopTaskChangeListener.onTaskOpening(task)
 
-    verify(desktopUserRepositories.current)
-        .addTask(task.displayId, task.taskId, task.isVisible)
-  }
+        verify(desktopUserRepositories.current).addTask(task.displayId, task.taskId, task.isVisible)
+    }
 
-  @Test
-  fun onTaskChanging_freeformTaskOutsideDesktop_removesTaskFromRepo() {
-    val task = createFullscreenTask().apply { isVisible = true }
-    whenever(desktopUserRepositories.current.isActiveTask(task.taskId))
-        .thenReturn(true)
+    @Test
+    fun onTaskChanging_freeformTaskOutsideDesktop_removesTaskFromRepo() {
+        val task = createFullscreenTask().apply { isVisible = true }
+        whenever(desktopUserRepositories.current.isActiveTask(task.taskId)).thenReturn(true)
 
-    desktopTaskChangeListener.onTaskChanging(task)
+        desktopTaskChangeListener.onTaskChanging(task)
 
-    verify(desktopUserRepositories.current)
-        .removeFreeformTask(task.displayId, task.taskId)
-  }
+        verify(desktopUserRepositories.current).removeFreeformTask(task.displayId, task.taskId)
+    }
 
-  @Test
-  fun onTaskChanging_visibleTaskInDesktop_updatesTaskVisibility() {
-    val task = createFreeformTask().apply { isVisible = true }
-    whenever(desktopUserRepositories.current.isActiveTask(task.taskId))
-        .thenReturn(true)
+    @Test
+    fun onTaskChanging_visibleTaskInDesktop_updatesTaskVisibility() {
+        val task = createFreeformTask().apply { isVisible = true }
+        whenever(desktopUserRepositories.current.isActiveTask(task.taskId)).thenReturn(true)
 
-    desktopTaskChangeListener.onTaskChanging(task)
+        desktopTaskChangeListener.onTaskChanging(task)
 
-    verify(desktopUserRepositories.current)
-        .updateTask(task.displayId, task.taskId, task.isVisible)
-  }
+        verify(desktopUserRepositories.current)
+            .updateTask(task.displayId, task.taskId, task.isVisible)
+    }
 
-  @Test
-  fun onTaskChanging_nonVisibleTask_updatesTaskVisibility() {
-    val task = createFreeformTask().apply { isVisible = false }
-    whenever(desktopUserRepositories.current.isActiveTask(task.taskId))
-        .thenReturn(true)
+    @Test
+    fun onTaskChanging_nonVisibleTask_updatesTaskVisibility() {
+        val task = createFreeformTask().apply { isVisible = false }
+        whenever(desktopUserRepositories.current.isActiveTask(task.taskId)).thenReturn(true)
 
-    desktopTaskChangeListener.onTaskChanging(task)
+        desktopTaskChangeListener.onTaskChanging(task)
 
-    verify(desktopUserRepositories.current)
-        .updateTask(task.displayId, task.taskId, task.isVisible)
-  }
+        verify(desktopUserRepositories.current)
+            .updateTask(task.displayId, task.taskId, task.isVisible)
+    }
 
-  @Test
-  fun onTaskMovingToFront_freeformTaskOutsideDesktop_removesTaskFromRepo() {
-    val task = createFullscreenTask().apply { isVisible = true }
-    whenever(desktopUserRepositories.current.isActiveTask(task.taskId))
-        .thenReturn(true)
+    @Test
+    fun onTaskMovingToFront_freeformTaskOutsideDesktop_removesTaskFromRepo() {
+        val task = createFullscreenTask().apply { isVisible = true }
+        whenever(desktopUserRepositories.current.isActiveTask(task.taskId)).thenReturn(true)
 
-    desktopTaskChangeListener.onTaskMovingToFront(task)
+        desktopTaskChangeListener.onTaskMovingToFront(task)
 
-    verify(desktopUserRepositories.current)
-        .removeFreeformTask(task.displayId, task.taskId)
-  }
+        verify(desktopUserRepositories.current).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(desktopUserRepositories.current.isActiveTask(task.taskId))
-        .thenReturn(true)
-    whenever(desktopUserRepositories.current.isClosingTask(task.taskId))
-        .thenReturn(false)
+    @Test
+    @EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION)
+    fun onTaskClosing_backNavEnabled_nonClosingTask_minimizesTaskInRepo() {
+        val task = createFreeformTask().apply { isVisible = true }
+        whenever(desktopUserRepositories.current.isActiveTask(task.taskId)).thenReturn(true)
+        whenever(desktopUserRepositories.current.isClosingTask(task.taskId)).thenReturn(false)
 
-    desktopTaskChangeListener.onTaskClosing(task)
+        desktopTaskChangeListener.onTaskClosing(task)
 
-    verify(desktopUserRepositories.current)
-        .updateTask(task.displayId, task.taskId, isVisible = false)
-    verify(desktopUserRepositories.current)
-        .minimizeTask(task.displayId, task.taskId)
-  }
+        verify(desktopUserRepositories.current)
+            .updateTask(task.displayId, task.taskId, isVisible = false)
+        verify(desktopUserRepositories.current).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(desktopUserRepositories.current.isActiveTask(task.taskId))
-        .thenReturn(true)
-    whenever(desktopUserRepositories.current.isClosingTask(task.taskId))
-        .thenReturn(true)
+    @Test
+    @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION)
+    fun onTaskClosing_backNavDisabled_closingTask_removesTaskInRepo() {
+        val task = createFreeformTask().apply { isVisible = true }
+        whenever(desktopUserRepositories.current.isActiveTask(task.taskId)).thenReturn(true)
+        whenever(desktopUserRepositories.current.isClosingTask(task.taskId)).thenReturn(true)
 
-    desktopTaskChangeListener.onTaskClosing(task)
+        desktopTaskChangeListener.onTaskClosing(task)
 
-    verify(desktopUserRepositories.current, never())
-        .minimizeTask(task.displayId, task.taskId)
-    verify(desktopUserRepositories.current)
-        .removeClosingTask(task.taskId)
-    verify(desktopUserRepositories.current)
-        .removeFreeformTask(task.displayId, task.taskId)
-  }
+        verify(desktopUserRepositories.current, never()).minimizeTask(task.displayId, task.taskId)
+        verify(desktopUserRepositories.current).removeClosingTask(task.taskId)
+        verify(desktopUserRepositories.current).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(desktopUserRepositories.current.isActiveTask(task.taskId))
-        .thenReturn(true)
-    whenever(desktopUserRepositories.current.isClosingTask(task.taskId))
-        .thenReturn(true)
+    @Test
+    @EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION)
+    fun onTaskClosing_backNavEnabled_closingTask_removesTaskFromRepo() {
+        val task = createFreeformTask().apply { isVisible = true }
+        whenever(desktopUserRepositories.current.isActiveTask(task.taskId)).thenReturn(true)
+        whenever(desktopUserRepositories.current.isClosingTask(task.taskId)).thenReturn(true)
 
-    desktopTaskChangeListener.onTaskClosing(task)
+        desktopTaskChangeListener.onTaskClosing(task)
 
-    verify(desktopUserRepositories.current).removeClosingTask(task.taskId)
-    verify(desktopUserRepositories.current)
-        .removeFreeformTask(task.displayId, task.taskId)
-  }
+        verify(desktopUserRepositories.current).removeClosingTask(task.taskId)
+        verify(desktopUserRepositories.current).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 33c1451..4f37180b 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
@@ -83,6 +83,8 @@
 import com.android.window.flags.Flags
 import com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE
 import com.android.window.flags.Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP
+import com.android.window.flags.Flags.FLAG_ENABLE_MOVE_TO_NEXT_DISPLAY_SHORTCUT
+import com.android.window.flags.Flags.FLAG_ENABLE_PER_DISPLAY_DESKTOP_WALLPAPER_ACTIVITY
 import com.android.wm.shell.MockToken
 import com.android.wm.shell.R
 import com.android.wm.shell.RootTaskDisplayAreaOrganizer
@@ -95,7 +97,6 @@
 import com.android.wm.shell.common.MultiInstanceHelper
 import com.android.wm.shell.common.ShellExecutor
 import com.android.wm.shell.common.SyncTransactionQueue
-import com.android.wm.shell.desktopmode.common.ToggleTaskSizeInteraction
 import com.android.wm.shell.desktopmode.DesktopImmersiveController.ExitResult
 import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.InputMethod
 import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ResizeTrigger
@@ -108,6 +109,7 @@
 import com.android.wm.shell.desktopmode.DesktopTestHelpers.createSplitScreenTask
 import com.android.wm.shell.desktopmode.EnterDesktopTaskTransitionHandler.FREEFORM_ANIMATION_DURATION
 import com.android.wm.shell.desktopmode.ExitDesktopTaskTransitionHandler.FULLSCREEN_ANIMATION_DURATION
+import com.android.wm.shell.desktopmode.common.ToggleTaskSizeInteraction
 import com.android.wm.shell.desktopmode.minimize.DesktopWindowLimitRemoteHandler
 import com.android.wm.shell.desktopmode.persistence.Desktop
 import com.android.wm.shell.desktopmode.persistence.DesktopPersistentRepository
@@ -136,8 +138,8 @@
 import com.android.wm.shell.windowdecor.tiling.DesktopTilingDecorViewModel
 import com.google.common.truth.Truth.assertThat
 import com.google.common.truth.Truth.assertWithMessage
-import java.util.function.Consumer
 import java.util.Optional
+import java.util.function.Consumer
 import junit.framework.Assert.assertFalse
 import junit.framework.Assert.assertTrue
 import kotlin.test.assertIs
@@ -166,8 +168,8 @@
 import org.mockito.Mockito.clearInvocations
 import org.mockito.Mockito.mock
 import org.mockito.Mockito.spy
-import org.mockito.Mockito.verify
 import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
 import org.mockito.kotlin.any
 import org.mockito.kotlin.anyOrNull
 import org.mockito.kotlin.argumentCaptor
@@ -188,4390 +190,4955 @@
 @EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
 class DesktopTasksControllerTest : ShellTestCase() {
 
-  @JvmField @Rule val setFlagsRule = SetFlagsRule()
+    @JvmField @Rule val setFlagsRule = SetFlagsRule()
 
-  @Mock lateinit var testExecutor: ShellExecutor
-  @Mock lateinit var shellCommandHandler: ShellCommandHandler
-  @Mock lateinit var shellController: ShellController
-  @Mock lateinit var displayController: DisplayController
-  @Mock lateinit var displayLayout: DisplayLayout
-  @Mock lateinit var shellTaskOrganizer: ShellTaskOrganizer
-  @Mock lateinit var syncQueue: SyncTransactionQueue
-  @Mock lateinit var rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer
-  @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
-  @Mock
-  lateinit var toggleResizeDesktopTaskTransitionHandler: ToggleResizeDesktopTaskTransitionHandler
-  @Mock lateinit var dragToDesktopTransitionHandler: DragToDesktopTransitionHandler
-  @Mock
-  lateinit var mMockDesktopImmersiveController: DesktopImmersiveController
-  @Mock lateinit var splitScreenController: SplitScreenController
-  @Mock lateinit var recentsTransitionHandler: RecentsTransitionHandler
-  @Mock lateinit var dragAndDropController: DragAndDropController
-  @Mock lateinit var multiInstanceHelper: MultiInstanceHelper
-  @Mock lateinit var desktopModeVisualIndicator: DesktopModeVisualIndicator
-  @Mock lateinit var recentTasksController: RecentTasksController
-  @Mock
-  private lateinit var mockInteractionJankMonitor: InteractionJankMonitor
-  @Mock private lateinit var mockSurface: SurfaceControl
-  @Mock private lateinit var taskbarDesktopTaskListener: TaskbarDesktopTaskListener
-  @Mock private lateinit var freeformTaskTransitionStarter: FreeformTaskTransitionStarter
-  @Mock private lateinit var mockHandler: Handler
-  @Mock private lateinit var desktopModeEventLogger: DesktopModeEventLogger
-  @Mock private lateinit var desktopModeUiEventLogger: DesktopModeUiEventLogger
-  @Mock lateinit var persistentRepository: DesktopPersistentRepository
-  @Mock lateinit var motionEvent: MotionEvent
-  @Mock lateinit var repositoryInitializer: DesktopRepositoryInitializer
-  @Mock private lateinit var mockToast: Toast
-  private lateinit var mockitoSession: StaticMockitoSession
-  @Mock
-  private lateinit var desktopTilingDecorViewModel: DesktopTilingDecorViewModel
-  @Mock
-  private lateinit var desktopWindowDecoration: DesktopModeWindowDecoration
-  @Mock private lateinit var resources: Resources
-  @Mock
-  lateinit var desktopModeEnterExitTransitionListener: DesktopModeEntryExitTransitionListener
-  @Mock private lateinit var userManager: UserManager
-  private lateinit var controller: DesktopTasksController
-  private lateinit var shellInit: ShellInit
-  private lateinit var taskRepository: DesktopRepository
-  private lateinit var userRepositories: DesktopUserRepositories
-  private lateinit var desktopTasksLimiter: DesktopTasksLimiter
-  private lateinit var recentsTransitionStateListener: RecentsTransitionStateListener
-  private lateinit var testScope: CoroutineScope
+    @Mock lateinit var testExecutor: ShellExecutor
+    @Mock lateinit var shellCommandHandler: ShellCommandHandler
+    @Mock lateinit var shellController: ShellController
+    @Mock lateinit var displayController: DisplayController
+    @Mock lateinit var displayLayout: DisplayLayout
+    @Mock lateinit var shellTaskOrganizer: ShellTaskOrganizer
+    @Mock lateinit var syncQueue: SyncTransactionQueue
+    @Mock lateinit var rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer
+    @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
+    @Mock
+    lateinit var toggleResizeDesktopTaskTransitionHandler: ToggleResizeDesktopTaskTransitionHandler
+    @Mock lateinit var dragToDesktopTransitionHandler: DragToDesktopTransitionHandler
+    @Mock lateinit var mMockDesktopImmersiveController: DesktopImmersiveController
+    @Mock lateinit var splitScreenController: SplitScreenController
+    @Mock lateinit var recentsTransitionHandler: RecentsTransitionHandler
+    @Mock lateinit var dragAndDropController: DragAndDropController
+    @Mock lateinit var multiInstanceHelper: MultiInstanceHelper
+    @Mock lateinit var desktopModeVisualIndicator: DesktopModeVisualIndicator
+    @Mock lateinit var recentTasksController: RecentTasksController
+    @Mock private lateinit var mockInteractionJankMonitor: InteractionJankMonitor
+    @Mock private lateinit var mockSurface: SurfaceControl
+    @Mock private lateinit var taskbarDesktopTaskListener: TaskbarDesktopTaskListener
+    @Mock private lateinit var freeformTaskTransitionStarter: FreeformTaskTransitionStarter
+    @Mock private lateinit var mockHandler: Handler
+    @Mock private lateinit var desktopModeEventLogger: DesktopModeEventLogger
+    @Mock private lateinit var desktopModeUiEventLogger: DesktopModeUiEventLogger
+    @Mock lateinit var persistentRepository: DesktopPersistentRepository
+    @Mock lateinit var motionEvent: MotionEvent
+    @Mock lateinit var repositoryInitializer: DesktopRepositoryInitializer
+    @Mock private lateinit var mockToast: Toast
+    private lateinit var mockitoSession: StaticMockitoSession
+    @Mock private lateinit var desktopTilingDecorViewModel: DesktopTilingDecorViewModel
+    @Mock private lateinit var desktopWindowDecoration: DesktopModeWindowDecoration
+    @Mock private lateinit var resources: Resources
+    @Mock
+    lateinit var desktopModeEnterExitTransitionListener: DesktopModeEntryExitTransitionListener
+    @Mock private lateinit var userManager: UserManager
+    private lateinit var controller: DesktopTasksController
+    private lateinit var shellInit: ShellInit
+    private lateinit var taskRepository: DesktopRepository
+    private lateinit var userRepositories: DesktopUserRepositories
+    private lateinit var desktopTasksLimiter: DesktopTasksLimiter
+    private lateinit var recentsTransitionStateListener: RecentsTransitionStateListener
+    private lateinit var testScope: CoroutineScope
 
-  private val shellExecutor = TestShellExecutor()
+    private val shellExecutor = TestShellExecutor()
 
-  // Mock running tasks are registered here so we can get the list from mock shell task organizer
-  private val runningTasks = mutableListOf<RunningTaskInfo>()
+    // Mock running tasks are registered here so we can get the list from mock shell task organizer
+    private val runningTasks = mutableListOf<RunningTaskInfo>()
 
-  private val DISPLAY_DIMENSION_SHORT = 1600
-  private val DISPLAY_DIMENSION_LONG = 2560
-  private val DEFAULT_LANDSCAPE_BOUNDS = Rect(320, 75, 2240, 1275)
-  private val DEFAULT_PORTRAIT_BOUNDS = Rect(200, 165, 1400, 2085)
-  private val RESIZABLE_LANDSCAPE_BOUNDS = Rect(25, 435, 1575, 1635)
-  private val RESIZABLE_PORTRAIT_BOUNDS = Rect(680, 75, 1880, 1275)
-  private val UNRESIZABLE_LANDSCAPE_BOUNDS = Rect(25, 449, 1575, 1611)
-  private val UNRESIZABLE_PORTRAIT_BOUNDS = Rect(830, 75, 1730, 1275)
+    private val DISPLAY_DIMENSION_SHORT = 1600
+    private val DISPLAY_DIMENSION_LONG = 2560
+    private val DEFAULT_LANDSCAPE_BOUNDS = Rect(320, 75, 2240, 1275)
+    private val DEFAULT_PORTRAIT_BOUNDS = Rect(200, 165, 1400, 2085)
+    private val RESIZABLE_LANDSCAPE_BOUNDS = Rect(25, 435, 1575, 1635)
+    private val RESIZABLE_PORTRAIT_BOUNDS = Rect(680, 75, 1880, 1275)
+    private val UNRESIZABLE_LANDSCAPE_BOUNDS = Rect(25, 449, 1575, 1611)
+    private val UNRESIZABLE_PORTRAIT_BOUNDS = Rect(830, 75, 1730, 1275)
 
-  @Before
-  fun setUp() {
-    Dispatchers.setMain(StandardTestDispatcher())
-    mockitoSession =
-        mockitoSession()
-            .strictness(Strictness.LENIENT)
-            .spyStatic(DesktopModeStatus::class.java)
-            .spyStatic(Toast::class.java)
-            .startMocking()
-    doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
+    @Before
+    fun setUp() {
+        Dispatchers.setMain(StandardTestDispatcher())
+        mockitoSession =
+            mockitoSession()
+                .strictness(Strictness.LENIENT)
+                .spyStatic(DesktopModeStatus::class.java)
+                .spyStatic(Toast::class.java)
+                .startMocking()
+        doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
 
-    testScope = CoroutineScope(Dispatchers.Unconfined + SupervisorJob())
-    shellInit = spy(ShellInit(testExecutor))
-    userRepositories =
-      DesktopUserRepositories(
-        context,
-        shellInit,
-        persistentRepository,
-        repositoryInitializer,
-        testScope,
-        userManager)
-    desktopTasksLimiter =
-        DesktopTasksLimiter(
-            transitions,
-            userRepositories,
-            shellTaskOrganizer,
-            MAX_TASK_LIMIT,
-            mockInteractionJankMonitor,
-            mContext,
-            mockHandler)
+        testScope = CoroutineScope(Dispatchers.Unconfined + SupervisorJob())
+        shellInit = spy(ShellInit(testExecutor))
+        userRepositories =
+            DesktopUserRepositories(
+                context,
+                shellInit,
+                shellController,
+                persistentRepository,
+                repositoryInitializer,
+                testScope,
+                userManager,
+            )
+        desktopTasksLimiter =
+            DesktopTasksLimiter(
+                transitions,
+                userRepositories,
+                shellTaskOrganizer,
+                MAX_TASK_LIMIT,
+                mockInteractionJankMonitor,
+                mContext,
+                mockHandler,
+            )
 
-    whenever(shellTaskOrganizer.getRunningTasks(anyInt())).thenAnswer { runningTasks }
-    whenever(transitions.startTransition(anyInt(), any(), isNull())).thenAnswer { Binder() }
-    whenever(enterDesktopTransitionHandler.moveToDesktop(any(), any())).thenAnswer { Binder() }
-    whenever(displayController.getDisplayLayout(anyInt())).thenReturn(displayLayout)
-    whenever(displayLayout.getStableBounds(any())).thenAnswer { i ->
-      (i.arguments.first() as Rect).set(STABLE_BOUNDS)
-    }
-    whenever(runBlocking { persistentRepository.readDesktop(any(), any()) }).thenReturn(
-      Desktop.getDefaultInstance()
-    )
-    doReturn(mockToast).`when` { Toast.makeText(any(), anyInt(), anyInt()) }
-
-    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>(), any()))
-      .thenReturn(ExitResult.NoExit)
-    whenever(mMockDesktopImmersiveController
-      .exitImmersiveIfApplicable(any(), anyInt(), anyOrNull(), any()))
-      .thenReturn(ExitResult.NoExit)
-
-    controller = createController()
-    controller.setSplitScreenController(splitScreenController)
-    controller.freeformTaskTransitionStarter = freeformTaskTransitionStarter
-    controller.desktopModeEnterExitTransitionListener = desktopModeEnterExitTransitionListener
-
-    shellInit.init()
-
-    val captor = ArgumentCaptor.forClass(RecentsTransitionStateListener::class.java)
-    verify(recentsTransitionHandler).addTransitionStateListener(captor.capture())
-    recentsTransitionStateListener = captor.value
-
-    controller.taskbarDesktopTaskListener = taskbarDesktopTaskListener
-
-    assumeTrue(ENABLE_SHELL_TRANSITIONS)
-
-    taskRepository = userRepositories.current
-  }
-
-  private fun createController(): DesktopTasksController {
-    return DesktopTasksController(
-        context,
-        shellInit,
-        shellCommandHandler,
-        shellController,
-        displayController,
-        shellTaskOrganizer,
-        syncQueue,
-        rootTaskDisplayAreaOrganizer,
-        dragAndDropController,
-        transitions,
-        keyguardManager,
-        mReturnToDragStartAnimator,
-        desktopMixedTransitionHandler,
-        enterDesktopTransitionHandler,
-        exitDesktopTransitionHandler,
-        dragAndDropTransitionHandler,
-        toggleResizeDesktopTaskTransitionHandler,
-        dragToDesktopTransitionHandler,
-        mMockDesktopImmersiveController,
-        userRepositories,
-        recentsTransitionHandler,
-        multiInstanceHelper,
-        shellExecutor,
-        Optional.of(desktopTasksLimiter),
-        recentTasksController,
-        mockInteractionJankMonitor,
-        mockHandler,
-        desktopModeEventLogger,
-        desktopModeUiEventLogger,
-        desktopTilingDecorViewModel,
-      )
-  }
-
-  @After
-  fun tearDown() {
-    mockitoSession.finishMocking()
-
-    runningTasks.clear()
-    testScope.cancel()
-  }
-
-  @Test
-  fun instantiate_addInitCallback() {
-    verify(shellInit).addInitCallback(any(), any<DesktopTasksController>())
-  }
-
-  @Test
-  fun doesAnyTaskRequireTaskbarRounding_onlyFreeFormTaskIsRunning_returnFalse() {
-    setUpFreeformTask()
-
-    assertThat(controller.doesAnyTaskRequireTaskbarRounding(DEFAULT_DISPLAY)).isFalse()
-  }
-
-  @Test
-  fun doesAnyTaskRequireTaskbarRounding_toggleResizeOfFreeFormTask_returnTrue() {
-    val task1 = setUpFreeformTask()
-
-    val argumentCaptor = ArgumentCaptor.forClass(Boolean::class.java)
-    controller.toggleDesktopTaskSize(
-      task1,
-      ToggleTaskSizeInteraction(
-        ToggleTaskSizeInteraction.Direction.MAXIMIZE,
-        ToggleTaskSizeInteraction.Source.HEADER_BUTTON_TO_MAXIMIZE,
-        InputMethod.TOUCH
-      )
-    )
-
-    verify(taskbarDesktopTaskListener).onTaskbarCornerRoundingUpdate(argumentCaptor.capture())
-    verify(desktopModeEventLogger, times(1)).logTaskResizingEnded(
-      ResizeTrigger.MAXIMIZE_BUTTON,
-      InputMethod.TOUCH,
-      task1,
-      STABLE_BOUNDS.width(),
-      STABLE_BOUNDS.height(),
-      displayController
-    )
-    assertThat(argumentCaptor.value).isTrue()
-  }
-
-  @Test
-  fun doesAnyTaskRequireTaskbarRounding_fullScreenTaskIsRunning_returnTrue() {
-    val stableBounds = Rect().apply { displayLayout.getStableBounds(this) }
-    setUpFreeformTask(bounds = stableBounds, active = true)
-    assertThat(controller.doesAnyTaskRequireTaskbarRounding(DEFAULT_DISPLAY)).isTrue()
-  }
-
-  @Test
-  fun doesAnyTaskRequireTaskbarRounding_toggleResizeOfMaximizedTask_returnFalse() {
-    val stableBounds = Rect().apply { displayLayout.getStableBounds(this) }
-    val task1 = setUpFreeformTask(bounds = stableBounds, active = true)
-
-    val argumentCaptor = ArgumentCaptor.forClass(Boolean::class.java)
-    controller.toggleDesktopTaskSize(
-      task1,
-      ToggleTaskSizeInteraction(
-        ToggleTaskSizeInteraction.Direction.RESTORE,
-        ToggleTaskSizeInteraction.Source.HEADER_BUTTON_TO_RESTORE,
-        InputMethod.TOUCH
-      )
-    )
-
-    verify(taskbarDesktopTaskListener).onTaskbarCornerRoundingUpdate(argumentCaptor.capture())
-    verify(desktopModeEventLogger, times(1)).logTaskResizingEnded(
-      eq(ResizeTrigger.MAXIMIZE_BUTTON),
-      eq(InputMethod.TOUCH),
-      eq(task1),
-      anyOrNull(),
-      anyOrNull(),
-      eq(displayController),
-      anyOrNull()
-    )
-    assertThat(argumentCaptor.value).isFalse()
-  }
-
-  @Test
-  fun doesAnyTaskRequireTaskbarRounding_splitScreenTaskIsRunning_returnTrue() {
-    val stableBounds = Rect().apply { displayLayout.getStableBounds(this) }
-    setUpFreeformTask(bounds = Rect(stableBounds.left, stableBounds.top, 500, stableBounds.bottom))
-
-    assertThat(controller.doesAnyTaskRequireTaskbarRounding(DEFAULT_DISPLAY)).isTrue()
-  }
-
-
-  @Test
-  fun instantiate_cannotEnterDesktopMode_doNotAddInitCallback() {
-    whenever(DesktopModeStatus.canEnterDesktopMode(context)).thenReturn(false)
-    clearInvocations(shellInit)
-
-    createController()
-
-    verify(shellInit, never()).addInitCallback(any(), any<DesktopTasksController>())
-  }
-
-  @Test
-  @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
-  fun showDesktopApps_allAppsInvisible_bringsToFront_desktopWallpaperDisabled() {
-    val homeTask = setUpHomeTask()
-    val task1 = setUpFreeformTask()
-    val task2 = setUpFreeformTask()
-    markTaskHidden(task1)
-    markTaskHidden(task2)
-
-    controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
-
-    val wct = getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
-    assertThat(wct.hierarchyOps).hasSize(3)
-    // Expect order to be from bottom: home, task1, task2
-    wct.assertReorderAt(index = 0, homeTask)
-    wct.assertReorderAt(index = 1, task1)
-    wct.assertReorderAt(index = 2, task2)
-  }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
-  fun showDesktopApps_allAppsInvisible_bringsToFront_desktopWallpaperEnabled() {
-    val task1 = setUpFreeformTask()
-    val task2 = setUpFreeformTask()
-    markTaskHidden(task1)
-    markTaskHidden(task2)
-
-    controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
-
-    val wct = getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
-    assertThat(wct.hierarchyOps).hasSize(3)
-    // Expect order to be from bottom: wallpaper intent, task1, task2
-    wct.assertPendingIntentAt(index = 0, desktopWallpaperIntent)
-    wct.assertReorderAt(index = 1, task1)
-    wct.assertReorderAt(index = 2, task2)
-  }
-
-  @Test
-  fun isDesktopModeShowing_noTasks_returnsFalse() {
-    assertThat(controller.isDesktopModeShowing(displayId = 0)).isFalse()
-  }
-
-  @Test
-  fun isDesktopModeShowing_noTasksVisible_returnsFalse() {
-    val task1 = setUpFreeformTask()
-    val task2 = setUpFreeformTask()
-    markTaskHidden(task1)
-    markTaskHidden(task2)
-
-    assertThat(controller.isDesktopModeShowing(displayId = 0)).isFalse()
-  }
-
-  @Test
-  fun isDesktopModeShowing_tasksActiveAndVisible_returnsTrue() {
-    val task1 = setUpFreeformTask()
-    val task2 = setUpFreeformTask()
-    markTaskVisible(task1)
-    markTaskHidden(task2)
-
-    assertThat(controller.isDesktopModeShowing(displayId = 0)).isTrue()
-  }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
-  fun showDesktopApps_onSecondaryDisplay_desktopWallpaperEnabled_shouldNotShowWallpaper() {
-    val homeTask = setUpHomeTask(SECOND_DISPLAY)
-    val task1 = setUpFreeformTask(SECOND_DISPLAY)
-    val task2 = setUpFreeformTask(SECOND_DISPLAY)
-    markTaskHidden(task1)
-    markTaskHidden(task2)
-
-    controller.showDesktopApps(SECOND_DISPLAY, RemoteTransition(TestRemoteTransition()))
-
-    val wct = getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
-    assertThat(wct.hierarchyOps).hasSize(3)
-    // Expect order to be from bottom: home, task1, task2 (no wallpaper intent)
-    wct.assertReorderAt(index = 0, homeTask)
-    wct.assertReorderAt(index = 1, task1)
-    wct.assertReorderAt(index = 2, task2)
-  }
-
-  @Test
-  @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
-  fun showDesktopApps_appsAlreadyVisible_bringsToFront_desktopWallpaperDisabled() {
-    val homeTask = setUpHomeTask()
-    val task1 = setUpFreeformTask()
-    val task2 = setUpFreeformTask()
-    markTaskVisible(task1)
-    markTaskVisible(task2)
-
-    controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
-
-    val wct = getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
-    assertThat(wct.hierarchyOps).hasSize(3)
-    // Expect order to be from bottom: home, task1, task2
-    wct.assertReorderAt(index = 0, homeTask)
-    wct.assertReorderAt(index = 1, task1)
-    wct.assertReorderAt(index = 2, task2)
-  }
-
-  @Test
-  @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
-  fun showDesktopApps_onSecondaryDisplay_desktopWallpaperDisabled_shouldNotMoveLauncher() {
-    val homeTask = setUpHomeTask(SECOND_DISPLAY)
-    val task1 = setUpFreeformTask(SECOND_DISPLAY)
-    val task2 = setUpFreeformTask(SECOND_DISPLAY)
-    markTaskHidden(task1)
-    markTaskHidden(task2)
-
-    controller.showDesktopApps(SECOND_DISPLAY, RemoteTransition(TestRemoteTransition()))
-
-    val wct = getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
-    assertThat(wct.hierarchyOps).hasSize(3)
-    // Expect order to be from bottom: home, task1, task2
-    wct.assertReorderAt(index = 0, homeTask)
-    wct.assertReorderAt(index = 1, task1)
-    wct.assertReorderAt(index = 2, task2)
-  }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
-  fun showDesktopApps_appsAlreadyVisible_bringsToFront_desktopWallpaperEnabled() {
-    val task1 = setUpFreeformTask()
-    val task2 = setUpFreeformTask()
-    markTaskVisible(task1)
-    markTaskVisible(task2)
-
-    controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
-
-    val wct = getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
-    assertThat(wct.hierarchyOps).hasSize(3)
-    // Expect order to be from bottom: wallpaper intent, task1, task2
-    wct.assertPendingIntentAt(index = 0, desktopWallpaperIntent)
-    wct.assertReorderAt(index = 1, task1)
-    wct.assertReorderAt(index = 2, task2)
-  }
-
-  @Test
-  @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
-  fun showDesktopApps_someAppsInvisible_reordersAll_desktopWallpaperDisabled() {
-    val homeTask = setUpHomeTask()
-    val task1 = setUpFreeformTask()
-    val task2 = setUpFreeformTask()
-    markTaskHidden(task1)
-    markTaskVisible(task2)
-
-    controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
-
-    val wct = getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
-    assertThat(wct.hierarchyOps).hasSize(3)
-    // Expect order to be from bottom: home, task1, task2
-    wct.assertReorderAt(index = 0, homeTask)
-    wct.assertReorderAt(index = 1, task1)
-    wct.assertReorderAt(index = 2, task2)
-  }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
-  fun showDesktopApps_someAppsInvisible_reordersAll_desktopWallpaperEnabled() {
-    val task1 = setUpFreeformTask()
-    val task2 = setUpFreeformTask()
-    markTaskHidden(task1)
-    markTaskVisible(task2)
-
-    controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
-
-    val wct = getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
-    assertThat(wct.hierarchyOps).hasSize(3)
-    // Expect order to be from bottom: wallpaper intent, task1, task2
-    wct.assertPendingIntentAt(index = 0, desktopWallpaperIntent)
-    wct.assertReorderAt(index = 1, task1)
-    wct.assertReorderAt(index = 2, task2)
-  }
-
-  @Test
-  @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
-  fun showDesktopApps_noActiveTasks_reorderHomeToTop_desktopWallpaperDisabled() {
-    val homeTask = setUpHomeTask()
-
-    controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
-
-    val wct = getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
-    assertThat(wct.hierarchyOps).hasSize(1)
-    wct.assertReorderAt(index = 0, homeTask)
-  }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
-  fun showDesktopApps_noActiveTasks_addDesktopWallpaper_desktopWallpaperEnabled() {
-    controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
-
-    val wct = getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
-    wct.assertPendingIntentAt(index = 0, desktopWallpaperIntent)
-  }
-
-  @Test
-  @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
-  fun showDesktopApps_twoDisplays_bringsToFrontOnlyOneDisplay_desktopWallpaperDisabled() {
-    val homeTaskDefaultDisplay = setUpHomeTask(DEFAULT_DISPLAY)
-    val taskDefaultDisplay = setUpFreeformTask(DEFAULT_DISPLAY)
-    setUpHomeTask(SECOND_DISPLAY)
-    val taskSecondDisplay = setUpFreeformTask(SECOND_DISPLAY)
-    markTaskHidden(taskDefaultDisplay)
-    markTaskHidden(taskSecondDisplay)
-
-    controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
-
-    val wct = getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
-    assertThat(wct.hierarchyOps).hasSize(2)
-    // Expect order to be from bottom: home, task
-    wct.assertReorderAt(index = 0, homeTaskDefaultDisplay)
-    wct.assertReorderAt(index = 1, taskDefaultDisplay)
-  }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
-  fun showDesktopApps_twoDisplays_bringsToFrontOnlyOneDisplay_desktopWallpaperEnabled() {
-    val homeTaskDefaultDisplay = setUpHomeTask(DEFAULT_DISPLAY)
-    val taskDefaultDisplay = setUpFreeformTask(DEFAULT_DISPLAY)
-    setUpHomeTask(SECOND_DISPLAY)
-    val taskSecondDisplay = setUpFreeformTask(SECOND_DISPLAY)
-    markTaskHidden(taskDefaultDisplay)
-    markTaskHidden(taskSecondDisplay)
-
-    controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
-
-    val wct = getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
-    assertThat(wct.hierarchyOps).hasSize(3)
-    // Move home to front
-    wct.assertReorderAt(index = 0, homeTaskDefaultDisplay)
-    // Add desktop wallpaper activity
-    wct.assertPendingIntentAt(index = 1, desktopWallpaperIntent)
-    // Move freeform task to front
-    wct.assertReorderAt(index = 2, taskDefaultDisplay)
-  }
-
-  @Test
-  @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
-  fun showDesktopApps_desktopWallpaperDisabled_dontReorderMinimizedTask() {
-    val homeTask = setUpHomeTask()
-    val freeformTask = setUpFreeformTask()
-    val minimizedTask = setUpFreeformTask()
-
-    markTaskHidden(freeformTask)
-    markTaskHidden(minimizedTask)
-    taskRepository.minimizeTask(DEFAULT_DISPLAY, minimizedTask.taskId)
-    controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
-
-    val wct = getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
-    assertThat(wct.hierarchyOps).hasSize(2)
-    // Reorder home and freeform task to top, don't reorder the minimized task
-    wct.assertReorderAt(index = 0, homeTask, toTop = true)
-    wct.assertReorderAt(index = 1, freeformTask, toTop = true)
-  }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
-  fun showDesktopApps_desktopWallpaperEnabled_dontReorderMinimizedTask() {
-    val homeTask = setUpHomeTask()
-    val freeformTask = setUpFreeformTask()
-    val minimizedTask = setUpFreeformTask()
-
-    markTaskHidden(freeformTask)
-    markTaskHidden(minimizedTask)
-    taskRepository.minimizeTask(DEFAULT_DISPLAY, minimizedTask.taskId)
-    controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
-
-    val wct = getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
-    assertThat(wct.hierarchyOps).hasSize(3)
-    // Move home to front
-    wct.assertReorderAt(index = 0, homeTask, toTop = true)
-    // Add desktop wallpaper activity
-    wct.assertPendingIntentAt(index = 1, desktopWallpaperIntent)
-    // Reorder freeform task to top, don't reorder the minimized task
-    wct.assertReorderAt(index = 2, freeformTask, toTop = true)
-  }
-
-  @Test
-  fun visibleTaskCount_noTasks_returnsZero() {
-    assertThat(controller.visibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(0)
-  }
-
-  @Test
-  fun visibleTaskCount_twoTasks_bothVisible_returnsTwo() {
-    setUpHomeTask()
-    setUpFreeformTask().also(::markTaskVisible)
-    setUpFreeformTask().also(::markTaskVisible)
-    assertThat(controller.visibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(2)
-  }
-
-  @Test
-  fun visibleTaskCount_twoTasks_oneVisible_returnsOne() {
-    setUpHomeTask()
-    setUpFreeformTask().also(::markTaskVisible)
-    setUpFreeformTask().also(::markTaskHidden)
-    assertThat(controller.visibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(1)
-  }
-
-  @Test
-  fun visibleTaskCount_twoTasksVisibleOnDifferentDisplays_returnsOne() {
-    setUpHomeTask()
-    setUpFreeformTask(DEFAULT_DISPLAY).also(::markTaskVisible)
-    setUpFreeformTask(SECOND_DISPLAY).also(::markTaskVisible)
-    assertThat(controller.visibleTaskCount(SECOND_DISPLAY)).isEqualTo(1)
-  }
-
-  @Test
-  fun addMoveToDesktopChanges_gravityLeft_noBoundsApplied() {
-    setUpLandscapeDisplay()
-    val task = setUpFullscreenTask(gravity = Gravity.LEFT)
-    val wct = WindowContainerTransaction()
-    controller.addMoveToDesktopChanges(wct, task)
-
-    val finalBounds = findBoundsChange(wct, task)
-    assertThat(finalBounds).isEqualTo(Rect())
-  }
-
-  @Test
-  fun addMoveToDesktopChanges_gravityRight_noBoundsApplied() {
-    setUpLandscapeDisplay()
-    val task = setUpFullscreenTask(gravity = Gravity.RIGHT)
-    val wct = WindowContainerTransaction()
-    controller.addMoveToDesktopChanges(wct, task)
-
-    val finalBounds = findBoundsChange(wct, task)
-    assertThat(finalBounds).isEqualTo(Rect())
-  }
-
-  @Test
-  fun addMoveToDesktopChanges_gravityTop_noBoundsApplied() {
-    setUpLandscapeDisplay()
-    val task = setUpFullscreenTask(gravity = Gravity.TOP)
-    val wct = WindowContainerTransaction()
-    controller.addMoveToDesktopChanges(wct, task)
-
-    val finalBounds = findBoundsChange(wct, task)
-    assertThat(finalBounds).isEqualTo(Rect())
-  }
-
-  @Test
-  fun addMoveToDesktopChanges_gravityBottom_noBoundsApplied() {
-    setUpLandscapeDisplay()
-    val task = setUpFullscreenTask(gravity = Gravity.BOTTOM)
-    val wct = WindowContainerTransaction()
-    controller.addMoveToDesktopChanges(wct, task)
-
-    val finalBounds = findBoundsChange(wct, task)
-    assertThat(finalBounds).isEqualTo(Rect())
-  }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_CASCADING_WINDOWS)
-  fun handleRequest_newFreeformTaskLaunch_cascadeApplied() {
-    setUpLandscapeDisplay()
-    val stableBounds = Rect()
-    displayLayout.getStableBoundsForDesktopMode(stableBounds)
-
-    setUpFreeformTask(bounds = DEFAULT_LANDSCAPE_BOUNDS)
-    val freeformTask = setUpFreeformTask(bounds = DEFAULT_LANDSCAPE_BOUNDS, active = false)
-
-    val wct = controller.handleRequest(Binder(), createTransition(freeformTask))
-
-    assertNotNull(wct, "should handle request")
-    val finalBounds = findBoundsChange(wct, freeformTask)
-    assertThat(stableBounds.getDesktopTaskPosition(finalBounds!!))
-      .isEqualTo(DesktopTaskPosition.BottomRight)
-  }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_CASCADING_WINDOWS)
-  fun handleRequest_freeformTaskAlreadyExistsInDesktopMode_cascadeNotApplied() {
-    setUpLandscapeDisplay()
-    val stableBounds = Rect()
-    displayLayout.getStableBoundsForDesktopMode(stableBounds)
-
-    setUpFreeformTask(bounds = DEFAULT_LANDSCAPE_BOUNDS)
-    val freeformTask = setUpFreeformTask(bounds = DEFAULT_LANDSCAPE_BOUNDS)
-
-    val wct = controller.handleRequest(Binder(), createTransition(freeformTask))
-
-    assertNull(wct, "should not handle request")
-  }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_CASCADING_WINDOWS)
-  fun addMoveToDesktopChanges_positionBottomRight() {
-    setUpLandscapeDisplay()
-    val stableBounds = Rect()
-    displayLayout.getStableBoundsForDesktopMode(stableBounds)
-
-    setUpFreeformTask(bounds = DEFAULT_LANDSCAPE_BOUNDS)
-
-    val task = setUpFullscreenTask()
-    val wct = WindowContainerTransaction()
-    controller.addMoveToDesktopChanges(wct, task)
-
-    val finalBounds = findBoundsChange(wct, task)
-    assertThat(stableBounds.getDesktopTaskPosition(finalBounds!!))
-      .isEqualTo(DesktopTaskPosition.BottomRight)
-  }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_CASCADING_WINDOWS)
-  fun addMoveToDesktopChanges_positionTopLeft() {
-    setUpLandscapeDisplay()
-    val stableBounds = Rect()
-    displayLayout.getStableBoundsForDesktopMode(stableBounds)
-
-    addFreeformTaskAtPosition(DesktopTaskPosition.BottomRight, stableBounds)
-
-    val task = setUpFullscreenTask()
-    val wct = WindowContainerTransaction()
-    controller.addMoveToDesktopChanges(wct, task)
-
-    val finalBounds = findBoundsChange(wct, task)
-    assertThat(stableBounds.getDesktopTaskPosition(finalBounds!!))
-      .isEqualTo(DesktopTaskPosition.TopLeft)
-  }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_CASCADING_WINDOWS)
-  fun addMoveToDesktopChanges_positionBottomLeft() {
-    setUpLandscapeDisplay()
-    val stableBounds = Rect()
-    displayLayout.getStableBoundsForDesktopMode(stableBounds)
-
-    addFreeformTaskAtPosition(DesktopTaskPosition.TopLeft, stableBounds)
-
-    val task = setUpFullscreenTask()
-    val wct = WindowContainerTransaction()
-    controller.addMoveToDesktopChanges(wct, task)
-
-    val finalBounds = findBoundsChange(wct, task)
-    assertThat(stableBounds.getDesktopTaskPosition(finalBounds!!))
-      .isEqualTo(DesktopTaskPosition.BottomLeft)
-  }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_CASCADING_WINDOWS)
-  fun addMoveToDesktopChanges_positionTopRight() {
-    setUpLandscapeDisplay()
-    val stableBounds = Rect()
-    displayLayout.getStableBoundsForDesktopMode(stableBounds)
-
-    addFreeformTaskAtPosition(DesktopTaskPosition.BottomLeft, stableBounds)
-
-    val task = setUpFullscreenTask()
-    val wct = WindowContainerTransaction()
-    controller.addMoveToDesktopChanges(wct, task)
-
-    val finalBounds = findBoundsChange(wct, task)
-    assertThat(stableBounds.getDesktopTaskPosition(finalBounds!!))
-      .isEqualTo(DesktopTaskPosition.TopRight)
-  }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_CASCADING_WINDOWS)
-  fun addMoveToDesktopChanges_positionResetsToCenter() {
-    setUpLandscapeDisplay()
-    val stableBounds = Rect()
-    displayLayout.getStableBoundsForDesktopMode(stableBounds)
-
-    addFreeformTaskAtPosition(DesktopTaskPosition.TopRight, stableBounds)
-
-    val task = setUpFullscreenTask()
-    val wct = WindowContainerTransaction()
-    controller.addMoveToDesktopChanges(wct, task)
-
-    val finalBounds = findBoundsChange(wct, task)
-    assertThat(stableBounds.getDesktopTaskPosition(finalBounds!!))
-      .isEqualTo(DesktopTaskPosition.Center)
-  }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_CASCADING_WINDOWS)
-  fun addMoveToDesktopChanges_lastWindowSnapLeft_positionResetsToCenter() {
-    setUpLandscapeDisplay()
-    val stableBounds = Rect()
-    displayLayout.getStableBoundsForDesktopMode(stableBounds)
-
-    // Add freeform task with half display size snap bounds at left side.
-    setUpFreeformTask(bounds = Rect(stableBounds.left, stableBounds.top, 500, stableBounds.bottom))
-
-    val task = setUpFullscreenTask()
-    val wct = WindowContainerTransaction()
-    controller.addMoveToDesktopChanges(wct, task)
-
-    val finalBounds = findBoundsChange(wct, task)
-    assertThat(stableBounds.getDesktopTaskPosition(finalBounds!!))
-      .isEqualTo(DesktopTaskPosition.Center)
-  }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_CASCADING_WINDOWS)
-  fun addMoveToDesktopChanges_lastWindowSnapRight_positionResetsToCenter() {
-    setUpLandscapeDisplay()
-    val stableBounds = Rect()
-    displayLayout.getStableBoundsForDesktopMode(stableBounds)
-
-    // Add freeform task with half display size snap bounds at right side.
-    setUpFreeformTask(bounds = Rect(
-      stableBounds.right - 500, stableBounds.top, stableBounds.right, stableBounds.bottom))
-
-    val task = setUpFullscreenTask()
-    val wct = WindowContainerTransaction()
-    controller.addMoveToDesktopChanges(wct, task)
-
-    val finalBounds = findBoundsChange(wct, task)
-    assertThat(stableBounds.getDesktopTaskPosition(finalBounds!!))
-      .isEqualTo(DesktopTaskPosition.Center)
-  }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_CASCADING_WINDOWS)
-  fun addMoveToDesktopChanges_lastWindowMaximised_positionResetsToCenter() {
-    setUpLandscapeDisplay()
-    val stableBounds = Rect()
-    displayLayout.getStableBoundsForDesktopMode(stableBounds)
-
-    // Add maximised freeform task.
-    setUpFreeformTask(bounds = Rect(stableBounds))
-
-    val task = setUpFullscreenTask()
-    val wct = WindowContainerTransaction()
-    controller.addMoveToDesktopChanges(wct, task)
-
-    val finalBounds = findBoundsChange(wct, task)
-    assertThat(stableBounds.getDesktopTaskPosition(finalBounds!!))
-      .isEqualTo(DesktopTaskPosition.Center)
-  }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_CASCADING_WINDOWS)
-  fun addMoveToDesktopChanges_defaultToCenterIfFree() {
-    setUpLandscapeDisplay()
-    val stableBounds = Rect()
-    displayLayout.getStableBoundsForDesktopMode(stableBounds)
-
-    val minTouchTarget = context.resources.getDimensionPixelSize(
-      R.dimen.freeform_required_visible_empty_space_in_header)
-    addFreeformTaskAtPosition(DesktopTaskPosition.Center, stableBounds,
-      Rect(0, 0, 1600, 1200), Point(0, minTouchTarget + 1))
-
-    val task = setUpFullscreenTask()
-    val wct = WindowContainerTransaction()
-    controller.addMoveToDesktopChanges(wct, task)
-
-    val finalBounds = findBoundsChange(wct, task)
-    assertThat(stableBounds.getDesktopTaskPosition(finalBounds!!))
-      .isEqualTo(DesktopTaskPosition.Center)
-  }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
-  fun addMoveToDesktopChanges_landscapeDevice_userFullscreenOverride_defaultPortraitBounds() {
-    setUpLandscapeDisplay()
-    val task = setUpFullscreenTask(enableUserFullscreenOverride = true)
-    val wct = WindowContainerTransaction()
-    controller.addMoveToDesktopChanges(wct, task)
-
-    assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_LANDSCAPE_BOUNDS)
-  }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
-  fun addMoveToDesktopChanges_landscapeDevice_systemFullscreenOverride_defaultPortraitBounds() {
-    setUpLandscapeDisplay()
-    val task = setUpFullscreenTask(enableSystemFullscreenOverride = true)
-    val wct = WindowContainerTransaction()
-    controller.addMoveToDesktopChanges(wct, task)
-
-    assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_LANDSCAPE_BOUNDS)
-  }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
-  fun addMoveToDesktopChanges_landscapeDevice_portraitResizableApp_aspectRatioOverridden() {
-    setUpLandscapeDisplay()
-    val task = setUpFullscreenTask(screenOrientation = SCREEN_ORIENTATION_PORTRAIT,
-      shouldLetterbox = true, aspectRatioOverrideApplied = true)
-    val wct = WindowContainerTransaction()
-    controller.addMoveToDesktopChanges(wct, task)
-
-    assertThat(findBoundsChange(wct, task)).isEqualTo(UNRESIZABLE_PORTRAIT_BOUNDS)
-  }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
-  fun addMoveToDesktopChanges_portraitDevice_userFullscreenOverride_defaultPortraitBounds() {
-    setUpPortraitDisplay()
-    val task = setUpFullscreenTask(enableUserFullscreenOverride = true)
-    val wct = WindowContainerTransaction()
-    controller.addMoveToDesktopChanges(wct, task)
-
-    assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_PORTRAIT_BOUNDS)
-  }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
-  fun addMoveToDesktopChanges_portraitDevice_systemFullscreenOverride_defaultPortraitBounds() {
-    setUpPortraitDisplay()
-    val task = setUpFullscreenTask(enableSystemFullscreenOverride = true)
-    val wct = WindowContainerTransaction()
-    controller.addMoveToDesktopChanges(wct, task)
-
-    assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_PORTRAIT_BOUNDS)
-  }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
-  fun addMoveToDesktopChanges_portraitDevice_landscapeResizableApp_aspectRatioOverridden() {
-    setUpPortraitDisplay()
-    val task = setUpFullscreenTask(screenOrientation = SCREEN_ORIENTATION_LANDSCAPE,
-      deviceOrientation = ORIENTATION_PORTRAIT,
-      shouldLetterbox = true, aspectRatioOverrideApplied = true)
-    val wct = WindowContainerTransaction()
-    controller.addMoveToDesktopChanges(wct, task)
-
-    assertThat(findBoundsChange(wct, task)).isEqualTo(UNRESIZABLE_LANDSCAPE_BOUNDS)
-  }
-
-  @Test
-  fun moveToDesktop_tdaFullscreen_windowingModeSetToFreeform() {
-    val task = setUpFullscreenTask()
-    val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!!
-    tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN
-    controller.moveRunningTaskToDesktop(task, transitionSource = UNKNOWN)
-    val wct = getLatestEnterDesktopWct()
-    assertThat(wct.changes[task.token.asBinder()]?.windowingMode).isEqualTo(WINDOWING_MODE_FREEFORM)
-    verify(desktopModeEnterExitTransitionListener).onEnterDesktopModeTransitionStarted(FREEFORM_ANIMATION_DURATION)
-  }
-
-  @Test
-  fun moveRunningTaskToDesktop_tdaFreeform_windowingModeSetToUndefined() {
-    val task = setUpFullscreenTask()
-    val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!!
-    tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM
-    controller.moveRunningTaskToDesktop(task, transitionSource = UNKNOWN)
-    val wct = getLatestEnterDesktopWct()
-    assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
-        .isEqualTo(WINDOWING_MODE_UNDEFINED)
-    verify(desktopModeEnterExitTransitionListener).onEnterDesktopModeTransitionStarted(FREEFORM_ANIMATION_DURATION)
-  }
-
-  @Test
-  fun moveTaskToDesktop_nonExistentTask_doesNothing() {
-    controller.moveTaskToDesktop(999, transitionSource = UNKNOWN)
-    verifyEnterDesktopWCTNotExecuted()
-    verify(desktopModeEnterExitTransitionListener, times(0)).onEnterDesktopModeTransitionStarted(anyInt())
-  }
-
-  @Test
-  @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
-  fun moveTaskToDesktop_desktopWallpaperDisabled_nonRunningTask_launchesInFreeform() {
-    val task = createTaskInfo(1)
-    whenever(shellTaskOrganizer.getRunningTaskInfo(anyInt())).thenReturn(null)
-    whenever(recentTasksController.findTaskInBackground(anyInt())).thenReturn(task)
-
-    controller.moveTaskToDesktop(task.taskId, transitionSource = UNKNOWN)
-
-    with(getLatestEnterDesktopWct()) {
-      assertLaunchTaskAt(0, task.taskId, WINDOWING_MODE_FREEFORM)
-    }
-  }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
-  fun moveTaskToDesktop_desktopWallpaperEnabled_nonRunningTask_launchesInFreeform() {
-    val task = createTaskInfo(1)
-    whenever(shellTaskOrganizer.getRunningTaskInfo(anyInt())).thenReturn(null)
-    whenever(recentTasksController.findTaskInBackground(anyInt())).thenReturn(task)
-
-    controller.moveTaskToDesktop(task.taskId, transitionSource = UNKNOWN)
-
-    with(getLatestEnterDesktopWct()) {
-      // Add desktop wallpaper activity
-      assertPendingIntentAt(index = 0, desktopWallpaperIntent)
-      // Launch task
-      assertLaunchTaskAt(index = 1, task.taskId, WINDOWING_MODE_FREEFORM)
-    }
-  }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
-  fun moveRunningTaskToDesktop_topActivityTranslucentWithoutDisplay_taskIsMovedToDesktop() {
-    val task =
-      setUpFullscreenTask().apply {
-        isActivityStackTransparent = true
-        isTopActivityNoDisplay = true
-        numActivities = 1
-      }
-
-    controller.moveRunningTaskToDesktop(task, transitionSource = UNKNOWN)
-    val wct = getLatestEnterDesktopWct()
-    assertThat(wct.changes[task.token.asBinder()]?.windowingMode).isEqualTo(WINDOWING_MODE_FREEFORM)
-    verify(desktopModeEnterExitTransitionListener).onEnterDesktopModeTransitionStarted(FREEFORM_ANIMATION_DURATION)
-  }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
-  fun moveRunningTaskToDesktop_topActivityTranslucentWithDisplay_doesNothing() {
-    val task =
-      setUpFullscreenTask().apply {
-        isActivityStackTransparent = true
-        isTopActivityNoDisplay = false
-        numActivities = 1
-      }
-
-    controller.moveRunningTaskToDesktop(task, transitionSource = UNKNOWN)
-    verifyEnterDesktopWCTNotExecuted()
-    verify(desktopModeEnterExitTransitionListener, times(0)).onEnterDesktopModeTransitionStarted(
-      FREEFORM_ANIMATION_DURATION
-    )
-  }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
-  fun moveRunningTaskToDesktop_systemUIActivityWithDisplay_doesNothing() {
-    // Set task as systemUI package
-    val systemUIPackageName = context.resources.getString(
-      com.android.internal.R.string.config_systemUi)
-    val baseComponent = ComponentName(systemUIPackageName, /* class */ "")
-    val task =
-      setUpFullscreenTask().apply {
-        baseActivity = baseComponent
-        isTopActivityNoDisplay = false
-      }
-
-    controller.moveRunningTaskToDesktop(task, transitionSource = UNKNOWN)
-    verifyEnterDesktopWCTNotExecuted()
-  }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
-  fun moveRunningTaskToDesktop_systemUIActivityWithoutDisplay_doesNothing() {
-    // Set task as systemUI package
-    val systemUIPackageName = context.resources.getString(
-      com.android.internal.R.string.config_systemUi)
-    val baseComponent = ComponentName(systemUIPackageName, /* class */ "")
-    val task =
-      setUpFullscreenTask().apply {
-        baseActivity = baseComponent
-        isTopActivityNoDisplay = true
-      }
-
-    controller.moveRunningTaskToDesktop(task, transitionSource = UNKNOWN)
-
-    val wct = getLatestEnterDesktopWct()
-    assertThat(wct.changes[task.token.asBinder()]?.windowingMode).isEqualTo(WINDOWING_MODE_FREEFORM)
-  }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
-  fun moveBackgroundTaskToDesktop_remoteTransition_usesOneShotHandler() {
-    val transitionHandlerArgCaptor = ArgumentCaptor.forClass(TransitionHandler::class.java)
-    whenever(
-      transitions.startTransition(anyInt(), any(), transitionHandlerArgCaptor.capture())
-    ).thenReturn(Binder())
-
-    val task = createTaskInfo(1)
-    whenever(shellTaskOrganizer.getRunningTaskInfo(anyInt())).thenReturn(null)
-    whenever(recentTasksController.findTaskInBackground(anyInt())).thenReturn(task)
-    controller.moveTaskToDesktop(
-      taskId = task.taskId,
-      transitionSource = UNKNOWN,
-      remoteTransition = RemoteTransition(spy(TestRemoteTransition())))
-
-    verify(desktopModeEnterExitTransitionListener).onEnterDesktopModeTransitionStarted(FREEFORM_ANIMATION_DURATION)
-    assertIs<OneShotRemoteHandler>(transitionHandlerArgCaptor.value)
-  }
-
-
-  @Test
-  fun moveRunningTaskToDesktop_remoteTransition_usesOneShotHandler() {
-    val transitionHandlerArgCaptor = ArgumentCaptor.forClass(TransitionHandler::class.java)
-    whenever(
-      transitions.startTransition(anyInt(), any(), transitionHandlerArgCaptor.capture())
-    ).thenReturn(Binder())
-
-    controller.moveRunningTaskToDesktop(
-      task = setUpFullscreenTask(),
-      transitionSource = UNKNOWN,
-      remoteTransition = RemoteTransition(spy(TestRemoteTransition())))
-
-    verify(desktopModeEnterExitTransitionListener).onEnterDesktopModeTransitionStarted(FREEFORM_ANIMATION_DURATION)
-    assertIs<OneShotRemoteHandler>(transitionHandlerArgCaptor.value)
-  }
-
-  @Test
-  @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
-  fun moveRunningTaskToDesktop_otherFreeformTasksBroughtToFront_desktopWallpaperDisabled() {
-    val homeTask = setUpHomeTask()
-    val freeformTask = setUpFreeformTask()
-    val fullscreenTask = setUpFullscreenTask()
-    markTaskHidden(freeformTask)
-
-    controller.moveRunningTaskToDesktop(fullscreenTask, transitionSource = UNKNOWN)
-
-    with(getLatestEnterDesktopWct()) {
-      // Operations should include home task, freeform task
-      assertThat(hierarchyOps).hasSize(3)
-      assertReorderSequence(homeTask, freeformTask, fullscreenTask)
-      assertThat(changes[fullscreenTask.token.asBinder()]?.windowingMode)
-          .isEqualTo(WINDOWING_MODE_FREEFORM)
-    }
-    verify(desktopModeEnterExitTransitionListener).onEnterDesktopModeTransitionStarted(FREEFORM_ANIMATION_DURATION)
-  }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
-  fun moveRunningTaskToDesktop_otherFreeformTasksBroughtToFront_desktopWallpaperEnabled() {
-    val freeformTask = setUpFreeformTask()
-    val fullscreenTask = setUpFullscreenTask()
-    markTaskHidden(freeformTask)
-
-    controller.moveRunningTaskToDesktop(fullscreenTask, transitionSource = UNKNOWN)
-
-    with(getLatestEnterDesktopWct()) {
-      // Operations should include wallpaper intent, freeform task, fullscreen task
-      assertThat(hierarchyOps).hasSize(3)
-      assertPendingIntentAt(index = 0, desktopWallpaperIntent)
-      assertReorderAt(index = 1, freeformTask)
-      assertReorderAt(index = 2, fullscreenTask)
-      assertThat(changes[fullscreenTask.token.asBinder()]?.windowingMode)
-          .isEqualTo(WINDOWING_MODE_FREEFORM)
-    }
-    verify(desktopModeEnterExitTransitionListener).onEnterDesktopModeTransitionStarted(FREEFORM_ANIMATION_DURATION)
-  }
-
-  @Test
-  fun moveRunningTaskToDesktop_onlyFreeformTasksFromCurrentDisplayBroughtToFront() {
-    setUpHomeTask(displayId = DEFAULT_DISPLAY)
-    val freeformTaskDefault = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
-    val fullscreenTaskDefault = setUpFullscreenTask(displayId = DEFAULT_DISPLAY)
-    markTaskHidden(freeformTaskDefault)
-
-    val homeTaskSecond = setUpHomeTask(displayId = SECOND_DISPLAY)
-    val freeformTaskSecond = setUpFreeformTask(displayId = SECOND_DISPLAY)
-    markTaskHidden(freeformTaskSecond)
-
-    controller.moveRunningTaskToDesktop(fullscreenTaskDefault, transitionSource = UNKNOWN)
-
-    with(getLatestEnterDesktopWct()) {
-      // Check that hierarchy operations do not include tasks from second display
-      assertThat(hierarchyOps.map { it.container }).doesNotContain(homeTaskSecond.token.asBinder())
-      assertThat(hierarchyOps.map { it.container })
-          .doesNotContain(freeformTaskSecond.token.asBinder())
-    }
-    verify(desktopModeEnterExitTransitionListener).onEnterDesktopModeTransitionStarted(FREEFORM_ANIMATION_DURATION)
-  }
-
-  @Test
-  fun moveRunningTaskToDesktop_splitTaskExitsSplit() {
-    val task = setUpSplitScreenTask()
-    controller.moveRunningTaskToDesktop(task, transitionSource = UNKNOWN)
-    val wct = getLatestEnterDesktopWct()
-    assertThat(wct.changes[task.token.asBinder()]?.windowingMode).isEqualTo(WINDOWING_MODE_FREEFORM)
-    verify(desktopModeEnterExitTransitionListener).onEnterDesktopModeTransitionStarted(FREEFORM_ANIMATION_DURATION)
-    verify(splitScreenController)
-        .prepareExitSplitScreen(any(), anyInt(), eq(SplitScreenController.EXIT_REASON_DESKTOP_MODE))
-  }
-
-  @Test
-  fun moveRunningTaskToDesktop_fullscreenTaskDoesNotExitSplit() {
-    val task = setUpFullscreenTask()
-    controller.moveRunningTaskToDesktop(task, transitionSource = UNKNOWN)
-    val wct = getLatestEnterDesktopWct()
-    assertThat(wct.changes[task.token.asBinder()]?.windowingMode).isEqualTo(WINDOWING_MODE_FREEFORM)
-    verify(desktopModeEnterExitTransitionListener).onEnterDesktopModeTransitionStarted(FREEFORM_ANIMATION_DURATION)
-    verify(splitScreenController, never())
-        .prepareExitSplitScreen(any(), anyInt(), eq(SplitScreenController.EXIT_REASON_DESKTOP_MODE))
-  }
-
-  @Test
-  @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
-  fun moveRunningTaskToDesktop_desktopWallpaperDisabled_bringsTasksOver_dontShowBackTask() {
-    val freeformTasks = (1..MAX_TASK_LIMIT).map { _ -> setUpFreeformTask() }
-    val newTask = setUpFullscreenTask()
-    val homeTask = setUpHomeTask()
-
-    controller.moveRunningTaskToDesktop(newTask, transitionSource = UNKNOWN)
-
-    val wct = getLatestEnterDesktopWct()
-    verify(desktopModeEnterExitTransitionListener).onEnterDesktopModeTransitionStarted(FREEFORM_ANIMATION_DURATION)
-    assertThat(wct.hierarchyOps.size).isEqualTo(MAX_TASK_LIMIT + 1) // visible tasks + home
-    wct.assertReorderAt(0, homeTask)
-    wct.assertReorderSequenceInRange(
-        range = 1..<(MAX_TASK_LIMIT + 1),
-        *freeformTasks.drop(1).toTypedArray(), // Skipping freeformTasks[0]
-        newTask)
-  }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
-  fun moveRunningTaskToDesktop_desktopWallpaperEnabled_bringsTasksOverLimit_dontShowBackTask() {
-    val freeformTasks = (1..MAX_TASK_LIMIT).map { _ -> setUpFreeformTask() }
-    val newTask = setUpFullscreenTask()
-    val homeTask = setUpHomeTask()
-
-    controller.moveRunningTaskToDesktop(newTask, transitionSource = UNKNOWN)
-
-    val wct = getLatestEnterDesktopWct()
-    verify(desktopModeEnterExitTransitionListener).onEnterDesktopModeTransitionStarted(FREEFORM_ANIMATION_DURATION)
-    assertThat(wct.hierarchyOps.size).isEqualTo(MAX_TASK_LIMIT + 2) // tasks + home + wallpaper
-    // Move home to front
-    wct.assertReorderAt(0, homeTask)
-    // Add desktop wallpaper activity
-    wct.assertPendingIntentAt(1, desktopWallpaperIntent)
-    // Bring freeform tasks to front
-    wct.assertReorderSequenceInRange(
-        range = 2..<(MAX_TASK_LIMIT + 2),
-        *freeformTasks.drop(1).toTypedArray(), // Skipping freeformTasks[0]
-        newTask)
-  }
-
-  @Test
-  fun moveToFullscreen_tdaFullscreen_windowingModeSetToUndefined() {
-    val task = setUpFreeformTask()
-    val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!!
-    tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN
-    controller.moveToFullscreen(task.taskId, transitionSource = UNKNOWN)
-    val wct = getLatestExitDesktopWct()
-    verify(desktopModeEnterExitTransitionListener, times(1)).onExitDesktopModeTransitionStarted(FULLSCREEN_ANIMATION_DURATION)
-    assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
-        .isEqualTo(WINDOWING_MODE_UNDEFINED)
-  }
-
-  @Test
-  fun moveToFullscreen_tdaFullscreen_windowingModeUndefined_removesWallpaperActivity() {
-    val task = setUpFreeformTask()
-    val wallpaperToken = MockToken().token()
-
-    taskRepository.wallpaperActivityToken = wallpaperToken
-    assertNotNull(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY))
-      .configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN
-
-    controller.moveToFullscreen(task.taskId, transitionSource = UNKNOWN)
-
-    val wct = getLatestExitDesktopWct()
-    val taskChange = assertNotNull(wct.changes[task.token.asBinder()])
-    verify(desktopModeEnterExitTransitionListener).onExitDesktopModeTransitionStarted(FULLSCREEN_ANIMATION_DURATION)
-    assertThat(taskChange.windowingMode).isEqualTo(WINDOWING_MODE_UNDEFINED)
-    // Removes wallpaper activity when leaving desktop
-    wct.assertRemoveAt(index = 0, wallpaperToken)
-  }
-
-  @Test
-  fun moveToFullscreen_tdaFreeform_windowingModeSetToFullscreen() {
-    val task = setUpFreeformTask()
-    val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!!
-    tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM
-    controller.moveToFullscreen(task.taskId, transitionSource = UNKNOWN)
-    val wct = getLatestExitDesktopWct()
-    assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
-        .isEqualTo(WINDOWING_MODE_FULLSCREEN)
-    verify(desktopModeEnterExitTransitionListener).onExitDesktopModeTransitionStarted(FULLSCREEN_ANIMATION_DURATION)
-  }
-
-  @Test
-  fun moveToFullscreen_tdaFreeform_windowingModeFullscreen_removesWallpaperActivity() {
-    val task = setUpFreeformTask()
-    val wallpaperToken = MockToken().token()
-
-    taskRepository.wallpaperActivityToken = wallpaperToken
-    assertNotNull(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY))
-      .configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM
-
-    controller.moveToFullscreen(task.taskId, transitionSource = UNKNOWN)
-
-    val wct = getLatestExitDesktopWct()
-    val taskChange = assertNotNull(wct.changes[task.token.asBinder()])
-    assertThat(taskChange.windowingMode).isEqualTo(WINDOWING_MODE_FULLSCREEN)
-    verify(desktopModeEnterExitTransitionListener).onExitDesktopModeTransitionStarted(FULLSCREEN_ANIMATION_DURATION)
-    // Removes wallpaper activity when leaving desktop
-    wct.assertRemoveAt(index = 0, wallpaperToken)
-  }
-
-  @Test
-  fun moveToFullscreen_multipleVisibleNonMinimizedTasks_doesNotRemoveWallpaperActivity() {
-    val task1 = setUpFreeformTask()
-    // Setup task2
-    setUpFreeformTask()
-    val wallpaperToken = MockToken().token()
-
-    taskRepository.wallpaperActivityToken = wallpaperToken
-    assertNotNull(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY))
-      .configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN
-
-    controller.moveToFullscreen(task1.taskId, transitionSource = UNKNOWN)
-
-    val wct = getLatestExitDesktopWct()
-    val task1Change = assertNotNull(wct.changes[task1.token.asBinder()])
-    assertThat(task1Change.windowingMode).isEqualTo(WINDOWING_MODE_UNDEFINED)
-    verify(desktopModeEnterExitTransitionListener).onExitDesktopModeTransitionStarted(FULLSCREEN_ANIMATION_DURATION)
-    // Does not remove wallpaper activity, as desktop still has a visible desktop task
-    assertThat(wct.hierarchyOps).isEmpty()
-  }
-
-  @Test
-  fun moveToFullscreen_nonExistentTask_doesNothing() {
-    controller.moveToFullscreen(999, transitionSource = UNKNOWN)
-    verifyExitDesktopWCTNotExecuted()
-  }
-
-  @Test
-  fun moveToFullscreen_secondDisplayTaskHasFreeform_secondDisplayNotAffected() {
-    val taskDefaultDisplay = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
-    val taskSecondDisplay = setUpFreeformTask(displayId = SECOND_DISPLAY)
-    controller.moveToFullscreen(taskDefaultDisplay.taskId, transitionSource = UNKNOWN)
-
-    with(getLatestExitDesktopWct()) {
-      assertThat(changes.keys).contains(taskDefaultDisplay.token.asBinder())
-      assertThat(changes.keys).doesNotContain(taskSecondDisplay.token.asBinder())
-    }
-    verify(desktopModeEnterExitTransitionListener).onExitDesktopModeTransitionStarted(FULLSCREEN_ANIMATION_DURATION)
-  }
-
-  @Test
-  fun moveTaskToFront_postsWctWithReorderOp() {
-    val task1 = setUpFreeformTask()
-    setUpFreeformTask()
-
-    controller.moveTaskToFront(task1, remoteTransition = null)
-
-    val wct = getLatestDesktopMixedTaskWct(type = TRANSIT_TO_FRONT)
-    assertThat(wct.hierarchyOps).hasSize(1)
-    wct.assertReorderAt(index = 0, task1)
-  }
-
-  @Test
-  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 = 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)
-  }
-
-  @Test
-  fun moveTaskToFront_remoteTransition_usesOneshotHandler() {
-    setUpHomeTask()
-    val freeformTasks = List(MAX_TASK_LIMIT) { setUpFreeformTask() }
-    val transitionHandlerArgCaptor = ArgumentCaptor.forClass(TransitionHandler::class.java)
-    whenever(
-      transitions.startTransition(anyInt(), any(), transitionHandlerArgCaptor.capture())
-    ).thenReturn(Binder())
-
-    controller.moveTaskToFront(freeformTasks[0], RemoteTransition(TestRemoteTransition()))
-
-    assertIs<OneShotRemoteHandler>(transitionHandlerArgCaptor.value)
-  }
-
-  @Test
-  fun moveTaskToFront_bringsTasksOverLimit_remoteTransition_usesWindowLimitHandler() {
-    setUpHomeTask()
-    val freeformTasks = List(MAX_TASK_LIMIT + 1) { setUpFreeformTask() }
-    val transitionHandlerArgCaptor = ArgumentCaptor.forClass(TransitionHandler::class.java)
-    whenever(
-      transitions.startTransition(anyInt(), any(), transitionHandlerArgCaptor.capture())
-    ).thenReturn(Binder())
-
-    controller.moveTaskToFront(freeformTasks[0], RemoteTransition(TestRemoteTransition()))
-
-    assertThat(transitionHandlerArgCaptor.value)
-      .isInstanceOf(DesktopWindowLimitRemoteHandler::class.java)
-  }
-
-  @Test
-  fun moveTaskToFront_backgroundTask_launchesTask() {
-    val task = createTaskInfo(1)
-    whenever(shellTaskOrganizer.getRunningTaskInfo(anyInt())).thenReturn(null)
-
-    controller.moveTaskToFront(task.taskId, remoteTransition = null)
-
-    val wct = getLatestDesktopMixedTaskWct(type = TRANSIT_OPEN)
-    assertThat(wct.hierarchyOps).hasSize(1)
-    wct.assertLaunchTaskAt(0, task.taskId, WINDOWING_MODE_FREEFORM)
-  }
-
-  @Test
-  fun moveTaskToFront_backgroundTaskBringsTasksOverLimit_minimizesBackTask() {
-    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 = 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)
-  }
-
-  @Test
-  fun moveToNextDisplay_noOtherDisplays() {
-    whenever(rootTaskDisplayAreaOrganizer.displayIds).thenReturn(intArrayOf(DEFAULT_DISPLAY))
-    val task = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
-    controller.moveToNextDisplay(task.taskId)
-    verifyWCTNotExecuted()
-  }
-
-  @Test
-  fun moveToNextDisplay_moveFromFirstToSecondDisplay() {
-    // Set up two display ids
-    whenever(rootTaskDisplayAreaOrganizer.displayIds)
-        .thenReturn(intArrayOf(DEFAULT_DISPLAY, SECOND_DISPLAY))
-    // Create a mock for the target display area: second display
-    val secondDisplayArea = DisplayAreaInfo(MockToken().token(), SECOND_DISPLAY, 0)
-    whenever(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(SECOND_DISPLAY))
-        .thenReturn(secondDisplayArea)
-
-    val task = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
-    controller.moveToNextDisplay(task.taskId)
-    with(getLatestWct(type = TRANSIT_CHANGE)) {
-      assertThat(hierarchyOps).hasSize(1)
-      assertThat(hierarchyOps[0].container).isEqualTo(task.token.asBinder())
-      assertThat(hierarchyOps[0].isReparent).isTrue()
-      assertThat(hierarchyOps[0].newParent).isEqualTo(secondDisplayArea.token.asBinder())
-      assertThat(hierarchyOps[0].toTop).isTrue()
-    }
-  }
-
-  @Test
-  fun moveToNextDisplay_moveFromSecondToFirstDisplay() {
-    // Set up two display ids
-    whenever(rootTaskDisplayAreaOrganizer.displayIds)
-        .thenReturn(intArrayOf(DEFAULT_DISPLAY, SECOND_DISPLAY))
-    // Create a mock for the target display area: default display
-    val defaultDisplayArea = DisplayAreaInfo(MockToken().token(), DEFAULT_DISPLAY, 0)
-    whenever(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY))
-        .thenReturn(defaultDisplayArea)
-
-    val task = setUpFreeformTask(displayId = SECOND_DISPLAY)
-    controller.moveToNextDisplay(task.taskId)
-
-    with(getLatestWct(type = TRANSIT_CHANGE)) {
-      assertThat(hierarchyOps).hasSize(1)
-      assertThat(hierarchyOps[0].container).isEqualTo(task.token.asBinder())
-      assertThat(hierarchyOps[0].isReparent).isTrue()
-      assertThat(hierarchyOps[0].newParent).isEqualTo(defaultDisplayArea.token.asBinder())
-      assertThat(hierarchyOps[0].toTop).isTrue()
-    }
-  }
-
-  @Test
-  fun getTaskWindowingMode() {
-    val fullscreenTask = setUpFullscreenTask()
-    val freeformTask = setUpFreeformTask()
-
-    assertThat(controller.getTaskWindowingMode(fullscreenTask.taskId))
-        .isEqualTo(WINDOWING_MODE_FULLSCREEN)
-    assertThat(controller.getTaskWindowingMode(freeformTask.taskId))
-        .isEqualTo(WINDOWING_MODE_FREEFORM)
-    assertThat(controller.getTaskWindowingMode(999)).isEqualTo(WINDOWING_MODE_UNDEFINED)
-  }
-
-  @Test
-  fun onDesktopWindowClose_noActiveTasks() {
-    val task = setUpFreeformTask(active = false)
-    val wct = WindowContainerTransaction()
-    controller.onDesktopWindowClose(wct, displayId = DEFAULT_DISPLAY, task)
-    // Doesn't modify transaction
-    assertThat(wct.hierarchyOps).isEmpty()
-  }
-
-  @Test
-  fun onDesktopWindowClose_singleActiveTask_noWallpaperActivityToken() {
-    val task = setUpFreeformTask()
-    val wct = WindowContainerTransaction()
-    controller.onDesktopWindowClose(wct, displayId = DEFAULT_DISPLAY, task)
-    // Doesn't modify transaction
-    assertThat(wct.hierarchyOps).isEmpty()
-  }
-
-  @Test
-  fun onDesktopWindowClose_singleActiveTask_hasWallpaperActivityToken() {
-    val task = setUpFreeformTask()
-    val wallpaperToken = MockToken().token()
-    taskRepository.wallpaperActivityToken = wallpaperToken
-
-    val wct = WindowContainerTransaction()
-    controller.onDesktopWindowClose(wct, displayId = DEFAULT_DISPLAY, task)
-    // Adds remove wallpaper operation
-    wct.assertRemoveAt(index = 0, wallpaperToken)
-  }
-
-  @Test
-  fun onDesktopWindowClose_singleActiveTask_isClosing() {
-    val task = setUpFreeformTask()
-    val wallpaperToken = MockToken().token()
-    taskRepository.wallpaperActivityToken = wallpaperToken
-    taskRepository.addClosingTask(DEFAULT_DISPLAY, task.taskId)
-
-    val wct = WindowContainerTransaction()
-    controller.onDesktopWindowClose(wct, displayId = DEFAULT_DISPLAY, task)
-    // Doesn't modify transaction
-    assertThat(wct.hierarchyOps).isEmpty()
-  }
-
-  @Test
-  fun onDesktopWindowClose_singleActiveTask_isMinimized() {
-    val task = setUpFreeformTask()
-    val wallpaperToken = MockToken().token()
-    taskRepository.wallpaperActivityToken = wallpaperToken
-    taskRepository.minimizeTask(DEFAULT_DISPLAY, task.taskId)
-
-    val wct = WindowContainerTransaction()
-    controller.onDesktopWindowClose(wct, displayId = DEFAULT_DISPLAY, task)
-    // Doesn't modify transaction
-    assertThat(wct.hierarchyOps).isEmpty()
-  }
-
-  @Test
-  fun onDesktopWindowClose_multipleActiveTasks() {
-    val task1 = setUpFreeformTask()
-    setUpFreeformTask()
-    val wallpaperToken = MockToken().token()
-    taskRepository.wallpaperActivityToken = wallpaperToken
-
-    val wct = WindowContainerTransaction()
-    controller.onDesktopWindowClose(wct, displayId = DEFAULT_DISPLAY, task1)
-    // Doesn't modify transaction
-    assertThat(wct.hierarchyOps).isEmpty()
-  }
-
-  @Test
-  fun onDesktopWindowClose_multipleActiveTasks_isOnlyNonClosingTask() {
-    val task1 = setUpFreeformTask()
-    val task2 = setUpFreeformTask()
-    val wallpaperToken = MockToken().token()
-    taskRepository.wallpaperActivityToken = wallpaperToken
-    taskRepository.addClosingTask(DEFAULT_DISPLAY, task2.taskId)
-
-    val wct = WindowContainerTransaction()
-    controller.onDesktopWindowClose(wct, displayId = DEFAULT_DISPLAY, task1)
-    // Adds remove wallpaper operation
-    wct.assertRemoveAt(index = 0, wallpaperToken)
-  }
-
-  @Test
-  fun onDesktopWindowClose_multipleActiveTasks_hasMinimized() {
-    val task1 = setUpFreeformTask()
-    val task2 = setUpFreeformTask()
-    val wallpaperToken = MockToken().token()
-    taskRepository.wallpaperActivityToken = wallpaperToken
-    taskRepository.minimizeTask(DEFAULT_DISPLAY, task2.taskId)
-
-    val wct = WindowContainerTransaction()
-    controller.onDesktopWindowClose(wct, displayId = DEFAULT_DISPLAY, task1)
-    // Adds remove wallpaper operation
-    wct.assertRemoveAt(index = 0, wallpaperToken)
-  }
-
-  @Test
-  fun onDesktopWindowMinimize_noActiveTask_doesntRemoveWallpaper() {
-    val task = setUpFreeformTask(active = false)
-    val transition = Binder()
-    whenever(freeformTaskTransitionStarter.startMinimizedModeTransition(any()))
-      .thenReturn(transition)
-    val wallpaperToken = MockToken().token()
-    taskRepository.wallpaperActivityToken = wallpaperToken
-
-    controller.minimizeTask(task)
-
-    val captor = ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
-    verify(freeformTaskTransitionStarter).startMinimizedModeTransition(captor.capture())
-    captor.value.hierarchyOps.none { hop ->
-      hop.type == HIERARCHY_OP_TYPE_REMOVE_TASK && hop.container == wallpaperToken.asBinder()
-    }
-  }
-
-  @Test
-  fun onDesktopWindowMinimize_pipTask_autoEnterEnabled_startPipTransition() {
-    val task = setUpPipTask(autoEnterEnabled = true)
-    val handler = mock(TransitionHandler::class.java)
-    whenever(freeformTaskTransitionStarter.startPipTransition(any()))
-      .thenReturn(Binder())
-    whenever(transitions.dispatchRequest(any(), any(), anyOrNull()))
-      .thenReturn(android.util.Pair(handler, WindowContainerTransaction())
-    )
-
-    controller.minimizeTask(task)
-
-    verify(freeformTaskTransitionStarter).startPipTransition(any())
-    verify(freeformTaskTransitionStarter, never()).startMinimizedModeTransition(any())
-  }
-
-  @Test
-  fun onDesktopWindowMinimize_pipTask_autoEnterDisabled_startMinimizeTransition() {
-    val task = setUpPipTask(autoEnterEnabled = false)
-    whenever(freeformTaskTransitionStarter.startMinimizedModeTransition(any()))
-      .thenReturn(Binder())
-
-    controller.minimizeTask(task)
-
-    verify(freeformTaskTransitionStarter).startMinimizedModeTransition(any())
-    verify(freeformTaskTransitionStarter, never()).startPipTransition(any())
-  }
-
-  @Test
-  fun onDesktopWindowMinimize_singleActiveTask_noWallpaperActivityToken_doesntRemoveWallpaper() {
-    val task = setUpFreeformTask(active = true)
-    val transition = Binder()
-    whenever(freeformTaskTransitionStarter.startMinimizedModeTransition(any()))
-      .thenReturn(transition)
-
-    controller.minimizeTask(task)
-
-    val captor = ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
-    verify(freeformTaskTransitionStarter).startMinimizedModeTransition(captor.capture())
-    captor.value.hierarchyOps.none { hop ->
-      hop.type == HIERARCHY_OP_TYPE_REMOVE_TASK
-    }
-  }
-
-  @Test
-  fun onTaskMinimize_singleActiveTask_hasWallpaperActivityToken_removesWallpaper() {
-    val task = setUpFreeformTask()
-    val transition = Binder()
-    whenever(freeformTaskTransitionStarter.startMinimizedModeTransition(any()))
-      .thenReturn(transition)
-    val wallpaperToken = MockToken().token()
-    taskRepository.wallpaperActivityToken = wallpaperToken
-
-    // The only active task is being minimized.
-    controller.minimizeTask(task)
-
-    val captor = ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
-    verify(freeformTaskTransitionStarter).startMinimizedModeTransition(captor.capture())
-    // Adds remove wallpaper operation
-    captor.value.assertRemoveAt(index = 0, wallpaperToken)
-  }
-
-  @Test
-  fun onDesktopWindowMinimize_singleActiveTask_alreadyMinimized_doesntRemoveWallpaper() {
-    val task = setUpFreeformTask()
-    val transition = Binder()
-    whenever(freeformTaskTransitionStarter.startMinimizedModeTransition(any()))
-      .thenReturn(transition)
-    val wallpaperToken = MockToken().token()
-    taskRepository.wallpaperActivityToken = wallpaperToken
-    taskRepository.minimizeTask(DEFAULT_DISPLAY, task.taskId)
-
-    // The only active task is already minimized.
-    controller.minimizeTask(task)
-
-    val captor = ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
-    verify(freeformTaskTransitionStarter).startMinimizedModeTransition(captor.capture())
-    captor.value.hierarchyOps.none { hop ->
-      hop.type == HIERARCHY_OP_TYPE_REMOVE_TASK && hop.container == wallpaperToken.asBinder()
-    }
-  }
-
-  @Test
-  fun onDesktopWindowMinimize_multipleActiveTasks_doesntRemoveWallpaper() {
-    val task1 = setUpFreeformTask(active = true)
-    setUpFreeformTask(active = true)
-    val transition = Binder()
-    whenever(freeformTaskTransitionStarter.startMinimizedModeTransition(any()))
-      .thenReturn(transition)
-    val wallpaperToken = MockToken().token()
-    taskRepository.wallpaperActivityToken = wallpaperToken
-
-    controller.minimizeTask(task1)
-
-    val captor = ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
-    verify(freeformTaskTransitionStarter).startMinimizedModeTransition(captor.capture())
-    captor.value.hierarchyOps.none { hop ->
-      hop.type == HIERARCHY_OP_TYPE_REMOVE_TASK && hop.container == wallpaperToken.asBinder()
-    }
-  }
-
-  @Test
-  fun onDesktopWindowMinimize_multipleActiveTasks_minimizesTheOnlyVisibleTask_removesWallpaper() {
-    val task1 = setUpFreeformTask(active = true)
-    val task2 = setUpFreeformTask(active = true)
-    val transition = Binder()
-    whenever(freeformTaskTransitionStarter.startMinimizedModeTransition(any()))
-      .thenReturn(transition)
-    val wallpaperToken = MockToken().token()
-    taskRepository.wallpaperActivityToken = wallpaperToken
-    taskRepository.minimizeTask(DEFAULT_DISPLAY, task2.taskId)
-
-    // task1 is the only visible task as task2 is minimized.
-    controller.minimizeTask(task1)
-    // Adds remove wallpaper operation
-    val captor = ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
-    verify(freeformTaskTransitionStarter).startMinimizedModeTransition(captor.capture())
-    // Adds remove wallpaper operation
-    captor.value.assertRemoveAt(index = 0, wallpaperToken)
-  }
-
-  @Test
-  fun onDesktopWindowMinimize_triesToExitImmersive() {
-    val task = setUpFreeformTask()
-    val transition = Binder()
-    whenever(freeformTaskTransitionStarter.startMinimizedModeTransition(any()))
-      .thenReturn(transition)
-
-    controller.minimizeTask(task)
-
-    verify(mMockDesktopImmersiveController).exitImmersiveIfApplicable(any(), eq(task), any())
-  }
-
-  @Test
-  fun onDesktopWindowMinimize_invokesImmersiveTransitionStartCallback() {
-    val task = setUpFreeformTask()
-    val transition = Binder()
-    val runOnTransit = RunOnStartTransitionCallback()
-    whenever(freeformTaskTransitionStarter.startMinimizedModeTransition(any()))
-      .thenReturn(transition)
-    whenever(mMockDesktopImmersiveController.exitImmersiveIfApplicable(any(), eq(task), any()))
-      .thenReturn(
-        ExitResult.Exit(
-        exitingTask = task.taskId,
-        runOnTransitionStart = runOnTransit,
-      ))
-
-    controller.minimizeTask(task)
-
-    assertThat(runOnTransit.invocations).isEqualTo(1)
-    assertThat(runOnTransit.lastInvoked).isEqualTo(transition)
-  }
-
-  @Test
-  fun handleRequest_fullscreenTask_freeformVisible_returnSwitchToFreeformWCT() {
-    val homeTask = setUpHomeTask()
-    val freeformTask = setUpFreeformTask()
-    markTaskVisible(freeformTask)
-    val fullscreenTask = createFullscreenTask()
-
-    val wct = controller.handleRequest(Binder(), createTransition(fullscreenTask))
-
-    assertNotNull(wct, "should handle request")
-    assertThat(wct.changes[fullscreenTask.token.asBinder()]?.windowingMode)
-        .isEqualTo(WINDOWING_MODE_FREEFORM)
-
-    assertThat(wct.hierarchyOps).hasSize(1)
-  }
-
-  @Test
-  fun handleRequest_fullscreenTaskWithTaskOnHome_freeformVisible_returnSwitchToFreeformWCT() {
-    val homeTask = setUpHomeTask()
-    val freeformTask = setUpFreeformTask()
-    markTaskVisible(freeformTask)
-    val fullscreenTask = createFullscreenTask()
-    fullscreenTask.baseIntent.setFlags(Intent.FLAG_ACTIVITY_TASK_ON_HOME)
-
-    val wct = controller.handleRequest(Binder(), createTransition(fullscreenTask))
-
-    assertNotNull(wct, "should handle request")
-    assertThat(wct.changes[fullscreenTask.token.asBinder()]?.windowingMode)
-      .isEqualTo(WINDOWING_MODE_FREEFORM)
-
-    // There are 5 hops that are happening in this case:
-    // 1. Moving the fullscreen task to top as we add moveToDesktop() changes
-    // 2. Bringing home task to front
-    // 3. Pending intent for the wallpaper
-    // 4. Bringing the existing freeform task to top
-    // 5. Bringing the fullscreen task back at the top
-    assertThat(wct.hierarchyOps).hasSize(5)
-    wct.assertReorderAt(1, homeTask, toTop = true)
-    wct.assertReorderAt(4, fullscreenTask, toTop = true)
-  }
-
-  @Test
-  fun handleRequest_fullscreenTaskToFreeform_underTaskLimit_dontMinimize() {
-    val freeformTask = setUpFreeformTask()
-    markTaskVisible(freeformTask)
-    val fullscreenTask = createFullscreenTask()
-
-    val wct = controller.handleRequest(Binder(), createTransition(fullscreenTask))
-
-    // Make sure we only reorder the new task to top (we don't reorder the old task to bottom)
-    assertThat(wct?.hierarchyOps?.size).isEqualTo(1)
-    wct!!.assertReorderAt(0, fullscreenTask, toTop = true)
-  }
-
-  @Test
-  fun handleRequest_fullscreenTaskToFreeform_bringsTasksOverLimit_otherTaskIsMinimized() {
-    val freeformTasks = (1..MAX_TASK_LIMIT).map { _ -> setUpFreeformTask() }
-    freeformTasks.forEach { markTaskVisible(it) }
-    val fullscreenTask = createFullscreenTask()
-
-    val wct = controller.handleRequest(Binder(), createTransition(fullscreenTask))
-
-    // Make sure we reorder the new task to top, and the back task to the bottom
-    assertThat(wct!!.hierarchyOps.size).isEqualTo(2)
-    wct.assertReorderAt(0, fullscreenTask, toTop = true)
-    wct.assertReorderAt(1, freeformTasks[0], toTop = false)
-  }
-
-  @Test
-  fun handleRequest_fullscreenTaskWithTaskOnHome_bringsTasksOverLimit_otherTaskIsMinimized() {
-    val freeformTasks = (1..MAX_TASK_LIMIT).map { _ -> setUpFreeformTask() }
-    freeformTasks.forEach { markTaskVisible(it) }
-    val fullscreenTask = createFullscreenTask()
-    fullscreenTask.baseIntent.setFlags(Intent.FLAG_ACTIVITY_TASK_ON_HOME)
-
-    val wct = controller.handleRequest(Binder(), createTransition(fullscreenTask))
-
-    // Make sure we reorder the new task to top, and the back task to the bottom
-    assertThat(wct!!.hierarchyOps.size).isEqualTo(9)
-    wct.assertReorderAt(0, fullscreenTask, toTop = true)
-    wct.assertReorderAt(8, freeformTasks[0], toTop = false)
-  }
-
-  @Test
-  fun handleRequest_fullscreenTaskWithTaskOnHome_beyondLimit_existingAndNewTasksAreMinimized() {
-    val minimizedTask = setUpFreeformTask()
-    taskRepository.minimizeTask(displayId = DEFAULT_DISPLAY, taskId = minimizedTask.taskId)
-    val freeformTasks = (1..MAX_TASK_LIMIT).map { _ -> setUpFreeformTask() }
-    freeformTasks.forEach { markTaskVisible(it) }
-    val homeTask = setUpHomeTask()
-    val fullscreenTask = createFullscreenTask()
-    fullscreenTask.baseIntent.setFlags(Intent.FLAG_ACTIVITY_TASK_ON_HOME)
-
-    val wct = controller.handleRequest(Binder(), createTransition(fullscreenTask))
-
-    assertThat(wct!!.hierarchyOps.size).isEqualTo(10)
-    wct.assertReorderAt(0, fullscreenTask, toTop = true)
-    // Make sure we reorder the home task to the top, desktop tasks to top of them and minimized
-    // task is under the home task.
-    wct.assertReorderAt(1, homeTask, toTop = true)
-    wct.assertReorderAt(9, freeformTasks[0], toTop = false)
-  }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
-  fun handleRequest_fullscreenTask_noTasks_enforceDesktop_freeformDisplay_returnFreeformWCT() {
-    whenever(DesktopModeStatus.enterDesktopByDefaultOnFreeformDisplay(context)).thenReturn(true)
-    val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!!
-    tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM
-
-    val fullscreenTask = createFullscreenTask()
-    val wct = controller.handleRequest(Binder(), createTransition(fullscreenTask))
-
-    assertNotNull(wct, "should handle request")
-    assertThat(wct.changes[fullscreenTask.token.asBinder()]?.windowingMode)
-        .isEqualTo(WINDOWING_MODE_UNDEFINED)
-    assertThat(wct.hierarchyOps).hasSize(3)
-    // There are 3 hops that are happening in this case:
-    // 1. Moving the fullscreen task to top as we add moveToDesktop() changes
-    // 2. Pending intent for the wallpaper
-    // 3. Bringing the fullscreen task back at the top
-    wct.assertPendingIntentAt(1, desktopWallpaperIntent)
-    wct.assertReorderAt(2, fullscreenTask, toTop = true)
-  }
-
-  @Test
-  fun handleRequest_fullscreenTask_noTasks_enforceDesktop_fullscreenDisplay_returnNull() {
-    whenever(DesktopModeStatus.enterDesktopByDefaultOnFreeformDisplay(context)).thenReturn(true)
-    val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!!
-    tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN
-
-    val fullscreenTask = createFullscreenTask()
-    val wct = controller.handleRequest(Binder(), createTransition(fullscreenTask))
-
-    assertThat(wct).isNull()
-  }
-
-  @Test
-  fun handleRequest_fullscreenTask_freeformNotVisible_returnNull() {
-    val freeformTask = setUpFreeformTask()
-    markTaskHidden(freeformTask)
-    val fullscreenTask = createFullscreenTask()
-    assertThat(controller.handleRequest(Binder(), createTransition(fullscreenTask))).isNull()
-  }
-
-  @Test
-  fun handleRequest_fullscreenTask_noOtherTasks_returnNull() {
-    val fullscreenTask = createFullscreenTask()
-    assertThat(controller.handleRequest(Binder(), createTransition(fullscreenTask))).isNull()
-  }
-
-  @Test
-  fun handleRequest_fullscreenTask_freeformTaskOnOtherDisplay_returnNull() {
-    val fullscreenTaskDefaultDisplay = createFullscreenTask(displayId = DEFAULT_DISPLAY)
-    createFreeformTask(displayId = SECOND_DISPLAY)
-
-    val result = controller.handleRequest(Binder(), createTransition(fullscreenTaskDefaultDisplay))
-    assertThat(result).isNull()
-  }
-
-  @Test
-  fun handleRequest_freeformTask_freeformVisible_aboveTaskLimit_minimize() {
-    val freeformTasks = (1..MAX_TASK_LIMIT).map { _ -> setUpFreeformTask() }
-    freeformTasks.forEach { markTaskVisible(it) }
-    val newFreeformTask = createFreeformTask()
-
-    val wct = controller.handleRequest(Binder(), createTransition(newFreeformTask, TRANSIT_OPEN))
-
-    assertThat(wct?.hierarchyOps?.size).isEqualTo(1)
-    wct!!.assertReorderAt(0, freeformTasks[0], toTop = false) // Reorder to the bottom
-  }
-
-  @Test
-  fun handleRequest_freeformTask_relaunchActiveTask_taskBecomesUndefined() {
-    val freeformTask = setUpFreeformTask()
-    markTaskHidden(freeformTask)
-
-    val wct =
-      controller.handleRequest(Binder(), createTransition(freeformTask))
-
-    // Should become undefined as the TDA is set to fullscreen. It will inherit from the TDA.
-    assertNotNull(wct, "should handle request")
-    assertThat(wct.changes[freeformTask.token.asBinder()]?.windowingMode)
-      .isEqualTo(WINDOWING_MODE_UNDEFINED)
-  }
-
-  @Test
-  fun handleRequest_freeformTask_relaunchTask_enforceDesktop_freeformDisplay_noWinModeChange() {
-    whenever(DesktopModeStatus.enterDesktopByDefaultOnFreeformDisplay(context)).thenReturn(true)
-    val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!!
-    tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM
-
-    val freeformTask = setUpFreeformTask()
-    markTaskHidden(freeformTask)
-    val wct = controller.handleRequest(Binder(), createTransition(freeformTask))
-
-    assertNotNull(wct, "should handle request")
-    assertFalse(wct.anyWindowingModeChange(freeformTask.token))
-  }
-
-  @Test
-  fun handleRequest_freeformTask_relaunchTask_enforceDesktop_fullscreenDisplay_becomesUndefined() {
-    whenever(DesktopModeStatus.enterDesktopByDefaultOnFreeformDisplay(context)).thenReturn(true)
-    val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!!
-    tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN
-
-    val freeformTask = setUpFreeformTask()
-    markTaskHidden(freeformTask)
-    val wct = controller.handleRequest(Binder(), createTransition(freeformTask))
-
-    assertNotNull(wct, "should handle request")
-    assertThat(wct.changes[freeformTask.token.asBinder()]?.windowingMode)
-      .isEqualTo(WINDOWING_MODE_UNDEFINED)
-  }
-
-  @Test
-  @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
-  fun handleRequest_freeformTask_desktopWallpaperDisabled_freeformNotVisible_reorderedToTop() {
-    val freeformTask1 = setUpFreeformTask()
-    val freeformTask2 = createFreeformTask()
-
-    markTaskHidden(freeformTask1)
-    val result =
-        controller.handleRequest(Binder(), createTransition(freeformTask2, type = TRANSIT_TO_FRONT))
-
-    assertNotNull(result, "Should handle request")
-    assertThat(result.hierarchyOps?.size).isEqualTo(2)
-    result.assertReorderAt(1, freeformTask2, toTop = true)
-  }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
-  fun handleRequest_freeformTask_desktopWallpaperEnabled_freeformNotVisible_reorderedToTop() {
-    val freeformTask1 = setUpFreeformTask()
-    val freeformTask2 = createFreeformTask()
-
-    markTaskHidden(freeformTask1)
-    val result =
-      controller.handleRequest(Binder(), createTransition(freeformTask2, type = TRANSIT_TO_FRONT))
-
-    assertNotNull(result, "Should handle request")
-    assertThat(result.hierarchyOps?.size).isEqualTo(3)
-    // Add desktop wallpaper activity
-    result.assertPendingIntentAt(0, desktopWallpaperIntent)
-    // Bring active desktop tasks to front
-    result.assertReorderAt(1, freeformTask1, toTop = true)
-    // Bring new task to front
-    result.assertReorderAt(2, freeformTask2, toTop = true)
-  }
-
-  @Test
-  @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
-  fun handleRequest_freeformTask_desktopWallpaperDisabled_noOtherTasks_reorderedToTop() {
-    val task = createFreeformTask()
-    val result = controller.handleRequest(Binder(), createTransition(task))
-
-    assertNotNull(result, "Should handle request")
-    assertThat(result.hierarchyOps?.size).isEqualTo(1)
-    result.assertReorderAt(0, task, toTop = true)
-  }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
-  fun handleRequest_freeformTask_desktopWallpaperEnabled_noOtherTasks_reorderedToTop() {
-    val task = createFreeformTask()
-    val result = controller.handleRequest(Binder(), createTransition(task))
-
-    assertNotNull(result, "Should handle request")
-    assertThat(result.hierarchyOps?.size).isEqualTo(2)
-    // Add desktop wallpaper activity
-    result.assertPendingIntentAt(0, desktopWallpaperIntent)
-    // Bring new task to front
-    result.assertReorderAt(1, task, toTop = true)
-  }
-
-  @Test
-  @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
-  fun handleRequest_freeformTask_dskWallpaperDisabled_freeformOnOtherDisplayOnly_reorderedToTop() {
-    val taskDefaultDisplay = createFreeformTask(displayId = DEFAULT_DISPLAY)
-    // Second display task
-    createFreeformTask(displayId = SECOND_DISPLAY)
-
-    val result = controller.handleRequest(Binder(), createTransition(taskDefaultDisplay))
-
-    assertNotNull(result, "Should handle request")
-    assertThat(result.hierarchyOps?.size).isEqualTo(1)
-    result.assertReorderAt(0, taskDefaultDisplay, toTop = true)
-  }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
-  fun handleRequest_freeformTask_dskWallpaperEnabled_freeformOnOtherDisplayOnly_reorderedToTop() {
-    val taskDefaultDisplay = createFreeformTask(displayId = DEFAULT_DISPLAY)
-    // Second display task
-    createFreeformTask(displayId = SECOND_DISPLAY)
-
-    val result = controller.handleRequest(Binder(), createTransition(taskDefaultDisplay))
-
-    assertNotNull(result, "Should handle request")
-    assertThat(result.hierarchyOps?.size).isEqualTo(2)
-    // Add desktop wallpaper activity
-    result.assertPendingIntentAt(0, desktopWallpaperIntent)
-    // Bring new task to front
-    result.assertReorderAt(1, taskDefaultDisplay, toTop = true)
-  }
-
-  @Test
-  fun handleRequest_freeformTask_alreadyInDesktop_noOverrideDensity_noConfigDensityChange() {
-    whenever(DesktopModeStatus.useDesktopOverrideDensity()).thenReturn(false)
-
-    val freeformTask1 = setUpFreeformTask()
-    markTaskVisible(freeformTask1)
-
-    val freeformTask2 = createFreeformTask()
-    val result =
-        controller.handleRequest(freeformTask2.token.asBinder(), createTransition(freeformTask2))
-    assertFalse(result.anyDensityConfigChange(freeformTask2.token))
-  }
-
-  @Test
-  fun handleRequest_freeformTask_alreadyInDesktop_overrideDensity_hasConfigDensityChange() {
-    whenever(DesktopModeStatus.useDesktopOverrideDensity()).thenReturn(true)
-
-    val freeformTask1 = setUpFreeformTask()
-    markTaskVisible(freeformTask1)
-
-    val freeformTask2 = createFreeformTask()
-    val result =
-        controller.handleRequest(freeformTask2.token.asBinder(), createTransition(freeformTask2))
-    assertTrue(result.anyDensityConfigChange(freeformTask2.token))
-  }
-
-  @Test
-  fun handleRequest_freeformTask_keyguardLocked_returnNull() {
-    whenever(keyguardManager.isKeyguardLocked).thenReturn(true)
-    val freeformTask = createFreeformTask(displayId = DEFAULT_DISPLAY)
-
-    val result = controller.handleRequest(Binder(), createTransition(freeformTask))
-
-    assertNull(result, "Should NOT handle request")
-  }
-
-  @Test
-  fun handleRequest_notOpenOrToFrontTransition_returnNull() {
-    val task =
-        TestRunningTaskInfoBuilder()
-            .setActivityType(ACTIVITY_TYPE_STANDARD)
-            .setWindowingMode(WINDOWING_MODE_FULLSCREEN)
-            .build()
-    val transition = createTransition(task = task, type = TRANSIT_CLOSE)
-    val result = controller.handleRequest(Binder(), transition)
-    assertThat(result).isNull()
-  }
-
-  @Test
-  fun handleRequest_noTriggerTask_returnNull() {
-    assertThat(controller.handleRequest(Binder(), createTransition(task = null))).isNull()
-  }
-
-  @Test
-  fun handleRequest_triggerTaskNotStandard_returnNull() {
-    val task = TestRunningTaskInfoBuilder().setActivityType(ACTIVITY_TYPE_HOME).build()
-    assertThat(controller.handleRequest(Binder(), createTransition(task))).isNull()
-  }
-
-  @Test
-  fun handleRequest_triggerTaskNotFullscreenOrFreeform_returnNull() {
-    val task =
-        TestRunningTaskInfoBuilder()
-            .setActivityType(ACTIVITY_TYPE_STANDARD)
-            .setWindowingMode(WINDOWING_MODE_MULTI_WINDOW)
-            .build()
-    assertThat(controller.handleRequest(Binder(), createTransition(task))).isNull()
-  }
-
-  @Test
-  fun handleRequest_recentsAnimationRunning_returnNull() {
-    // Set up a visible freeform task so a fullscreen task should be converted to freeform
-    val freeformTask = setUpFreeformTask()
-    markTaskVisible(freeformTask)
-
-    // Mark recents animation running
-    recentsTransitionStateListener.onTransitionStateChanged(TRANSITION_STATE_ANIMATING)
-
-    // Open a fullscreen task, check that it does not result in a WCT with changes to it
-    val fullscreenTask = createFullscreenTask()
-    assertThat(controller.handleRequest(Binder(), createTransition(fullscreenTask))).isNull()
-  }
-
-  @Test
-  fun handleRequest_recentsAnimationRunning_relaunchActiveTask_taskBecomesUndefined() {
-    // Set up a visible freeform task
-    val freeformTask = setUpFreeformTask()
-    markTaskVisible(freeformTask)
-
-    // Mark recents animation running
-    recentsTransitionStateListener.onTransitionStateChanged(TRANSITION_STATE_ANIMATING)
-
-    // Should become undefined as the TDA is set to fullscreen. It will inherit from the TDA.
-    val result = controller.handleRequest(Binder(), createTransition(freeformTask))
-    assertThat(result?.changes?.get(freeformTask.token.asBinder())?.windowingMode)
-      .isEqualTo(WINDOWING_MODE_UNDEFINED)
-  }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
-  fun handleRequest_topActivityTransparentWithoutDisplay_returnSwitchToFreeformWCT() {
-    val freeformTask = setUpFreeformTask()
-    markTaskVisible(freeformTask)
-
-    val task =
-      setUpFullscreenTask().apply {
-        isActivityStackTransparent = true
-        isTopActivityNoDisplay = true
-        numActivities = 1
-      }
-
-    val result = controller.handleRequest(Binder(), createTransition(task))
-    assertThat(result?.changes?.get(task.token.asBinder())?.windowingMode)
-            .isEqualTo(WINDOWING_MODE_FREEFORM)
-  }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
-  fun handleRequest_topActivityTransparentWithDisplay_returnSwitchToFullscreenWCT() {
-    val freeformTask = setUpFreeformTask()
-    markTaskVisible(freeformTask)
-
-    val task =
-      setUpFreeformTask().apply {
-        isActivityStackTransparent = true
-        isTopActivityNoDisplay = false
-        numActivities = 1
-      }
-
-    val result = controller.handleRequest(Binder(), createTransition(task))
-    assertThat(result?.changes?.get(task.token.asBinder())?.windowingMode)
-            .isEqualTo(WINDOWING_MODE_UNDEFINED) // inherited FULLSCREEN
-  }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
-  fun handleRequest_systemUIActivityWithDisplay_returnSwitchToFullscreenWCT() {
-    val freeformTask = setUpFreeformTask()
-    markTaskVisible(freeformTask)
-
-    // Set task as systemUI package
-    val systemUIPackageName = context.resources.getString(
-      com.android.internal.R.string.config_systemUi)
-    val baseComponent = ComponentName(systemUIPackageName, /* class */ "")
-    val task =
-      setUpFreeformTask().apply {
-        baseActivity = baseComponent
-        isTopActivityNoDisplay = false
-      }
-
-    val result = controller.handleRequest(Binder(), createTransition(task))
-    assertThat(result?.changes?.get(task.token.asBinder())?.windowingMode)
-            .isEqualTo(WINDOWING_MODE_UNDEFINED) // inherited FULLSCREEN
-  }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
-  fun handleRequest_systemUIActivityWithoutDisplay_returnSwitchToFreeformWCT() {
-    val freeformTask = setUpFreeformTask()
-    markTaskVisible(freeformTask)
-
-    // Set task as systemUI package
-    val systemUIPackageName = context.resources.getString(
-      com.android.internal.R.string.config_systemUi)
-    val baseComponent = ComponentName(systemUIPackageName, /* class */ "")
-    val task =
-      setUpFullscreenTask().apply {
-        baseActivity = baseComponent
-        isTopActivityNoDisplay = true
-      }
-
-    val result = controller.handleRequest(Binder(), createTransition(task))
-    assertThat(result?.changes?.get(task.token.asBinder())?.windowingMode)
-      .isEqualTo(WINDOWING_MODE_FREEFORM)
-  }
-
-  @Test
-  @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,)
-  fun handleRequest_backTransition_singleTaskNoToken_noWallpaper_doesNotHandle() {
-    val task = setUpFreeformTask()
-
-    val result = controller.handleRequest(Binder(), createTransition(task, type = TRANSIT_TO_BACK))
-
-    assertNull(result, "Should not handle request")
-  }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,)
-  fun handleRequest_backTransition_singleTaskNoToken_withWallpaper_removesTask() {
-    val task = setUpFreeformTask()
-
-    val result = controller.handleRequest(Binder(), createTransition(task, type = TRANSIT_TO_BACK))
-
-    assertNull(result, "Should not handle request")
-  }
-
-  @Test
-  @EnableFlags(
-    Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,
-  )
-  fun handleRequest_backTransition_singleTaskNoToken_withWallpaper_notInDesktop_doesNotHandle() {
-    val task = setUpFreeformTask()
-    markTaskHidden(task)
-
-    val result = controller.handleRequest(Binder(), createTransition(task, type = TRANSIT_TO_BACK))
-
-    assertNull(result, "Should not handle request")
-  }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
-  fun handleRequest_backTransition_singleTaskNoToken_doesNotHandle() {
-    val task = setUpFreeformTask()
-
-    val result = controller.handleRequest(Binder(), createTransition(task, type = TRANSIT_TO_BACK))
-
-    assertNull(result, "Should not handle request")
-  }
-
-  @Test
-  @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,)
-  fun handleRequest_backTransition_singleTaskWithToken_noWallpaper_doesNotHandle() {
-    val task = setUpFreeformTask()
-
-    taskRepository.wallpaperActivityToken = MockToken().token()
-    val result = controller.handleRequest(Binder(), createTransition(task, type = TRANSIT_TO_BACK))
-
-    assertNull(result, "Should not handle request")
-  }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
-  fun handleRequest_backTransition_singleTaskWithToken_removesWallpaper() {
-    val task = setUpFreeformTask()
-    val wallpaperToken = MockToken().token()
-
-    taskRepository.wallpaperActivityToken = wallpaperToken
-    val result = controller.handleRequest(Binder(), createTransition(task, type = TRANSIT_TO_BACK))
-
-    // Should create remove wallpaper transaction
-    assertNotNull(result, "Should handle request").assertRemoveAt(index = 0, wallpaperToken)
-  }
-
-  @Test
-  @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,)
-  fun handleRequest_backTransition_multipleTasks_noWallpaper_doesNotHandle() {
-    val task1 = setUpFreeformTask()
-    setUpFreeformTask()
-
-    taskRepository.wallpaperActivityToken = MockToken().token()
-    val result = controller.handleRequest(Binder(), createTransition(task1, type = TRANSIT_TO_BACK))
-
-    assertNull(result, "Should not handle request")
-  }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
-  fun handleRequest_backTransition_multipleTasks_doesNotHandle() {
-    val task1 = setUpFreeformTask()
-    setUpFreeformTask()
-
-    taskRepository.wallpaperActivityToken = MockToken().token()
-    val result = controller.handleRequest(Binder(), createTransition(task1, type = TRANSIT_TO_BACK))
-
-    assertNull(result, "Should not handle request")
-  }
-
-  @Test
-  @EnableFlags(
-    Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,
-    Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION
-  )
-  fun handleRequest_backTransition_multipleTasksSingleNonClosing_removesWallpaperAndTask() {
-    val task1 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
-    val task2 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
-    val wallpaperToken = MockToken().token()
-
-    taskRepository.wallpaperActivityToken = wallpaperToken
-    taskRepository.addClosingTask(displayId = DEFAULT_DISPLAY, taskId = task2.taskId)
-    val result = controller.handleRequest(Binder(), createTransition(task1, type = TRANSIT_TO_BACK))
-
-    // Should create remove wallpaper transaction
-    assertNotNull(result, "Should handle request").assertRemoveAt(index = 0, wallpaperToken)
-  }
-
-  @Test
-  @EnableFlags(
-    Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,
-  )
-  fun handleRequest_backTransition_multipleTasksSingleNonMinimized_removesWallpaperAndTask() {
-    val task1 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
-    val task2 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
-    val wallpaperToken = MockToken().token()
-
-    taskRepository.wallpaperActivityToken = wallpaperToken
-    taskRepository.minimizeTask(displayId = DEFAULT_DISPLAY, taskId = task2.taskId)
-    val result = controller.handleRequest(Binder(), createTransition(task1, type = TRANSIT_TO_BACK))
-
-    // Should create remove wallpaper transaction
-    assertNotNull(result, "Should handle request").assertRemoveAt(index = 0, wallpaperToken)
-  }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,)
-  fun handleRequest_backTransition_nonMinimizadTask_withWallpaper_removesWallpaper() {
-    val task1 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
-    val task2 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
-    val wallpaperToken = MockToken().token()
-
-    taskRepository.wallpaperActivityToken = wallpaperToken
-    taskRepository.minimizeTask(displayId = DEFAULT_DISPLAY, taskId = task2.taskId)
-    // Task is being minimized so mark it as not visible.
-    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")
-  }
-
-  @Test
-  @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,)
-  fun handleRequest_closeTransition_singleTaskNoToken_noWallpaper_doesNotHandle() {
-    val task = setUpFreeformTask()
-
-    val result = controller.handleRequest(Binder(), createTransition(task, type = TRANSIT_CLOSE))
-
-    assertNull(result, "Should not handle request")
-  }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
-  fun handleRequest_closeTransition_singleTaskNoToken_doesNotHandle() {
-    val task = setUpFreeformTask()
-
-    val result = controller.handleRequest(Binder(), createTransition(task, type = TRANSIT_CLOSE))
-
-    assertNull(result, "Should not handle request")
-  }
-
-  @Test
-  @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
-  fun handleRequest_closeTransition_singleTaskWithToken_noWallpaper_doesNotHandle() {
-    val task = setUpFreeformTask()
-
-    taskRepository.wallpaperActivityToken = MockToken().token()
-    val result = controller.handleRequest(Binder(), createTransition(task, type = TRANSIT_CLOSE))
-
-    assertNull(result, "Should not handle request")
-  }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
-  fun handleRequest_closeTransition_singleTaskWithToken_withWallpaper_removesWallpaper() {
-    val task = setUpFreeformTask()
-    val wallpaperToken = MockToken().token()
-
-    taskRepository.wallpaperActivityToken = wallpaperToken
-    val result = controller.handleRequest(Binder(), createTransition(task, type = TRANSIT_CLOSE))
-
-    // Should create remove wallpaper transaction
-    assertNotNull(result, "Should handle request").assertRemoveAt(index = 0, wallpaperToken)
-  }
-
-  @Test
-  @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
-  fun handleRequest_closeTransition_multipleTasks_noWallpaper_doesNotHandle() {
-    val task1 = setUpFreeformTask()
-    setUpFreeformTask()
-
-    taskRepository.wallpaperActivityToken = MockToken().token()
-    val result = controller.handleRequest(Binder(), createTransition(task1, type = TRANSIT_CLOSE))
-
-    assertNull(result, "Should not handle request")
-  }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
-  fun handleRequest_closeTransition_multipleTasksFlagEnabled_doesNotHandle() {
-    val task1 = setUpFreeformTask()
-    setUpFreeformTask()
-
-    taskRepository.wallpaperActivityToken = MockToken().token()
-    val result = controller.handleRequest(Binder(), createTransition(task1, type = TRANSIT_CLOSE))
-
-    assertNull(result, "Should not handle request")
-  }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
-  fun handleRequest_closeTransition_multipleTasksSingleNonClosing_removesWallpaper() {
-    val task1 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
-    val task2 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
-    val wallpaperToken = MockToken().token()
-
-    taskRepository.wallpaperActivityToken = wallpaperToken
-    taskRepository.addClosingTask(displayId = DEFAULT_DISPLAY, taskId = task2.taskId)
-    val result = controller.handleRequest(Binder(), createTransition(task1, type = TRANSIT_CLOSE))
-
-    // Should create remove wallpaper transaction
-    assertNotNull(result, "Should handle request").assertRemoveAt(index = 0, wallpaperToken)
-  }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
-  fun handleRequest_closeTransition_multipleTasksSingleNonMinimized_removesWallpaper() {
-    val task1 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
-    val task2 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
-    val wallpaperToken = MockToken().token()
-
-    taskRepository.wallpaperActivityToken = wallpaperToken
-    taskRepository.minimizeTask(displayId = DEFAULT_DISPLAY, taskId = task2.taskId)
-    val result = controller.handleRequest(Binder(), createTransition(task1, type = TRANSIT_CLOSE))
-
-    // Should create remove wallpaper transaction
-    assertNotNull(result, "Should handle request").assertRemoveAt(index = 0, wallpaperToken)
-  }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,)
-  fun handleRequest_closeTransition_minimizadTask_withWallpaper_removesWallpaper() {
-    val task1 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
-    val task2 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
-    val wallpaperToken = MockToken().token()
-
-    taskRepository.wallpaperActivityToken = wallpaperToken
-    taskRepository.minimizeTask(displayId = DEFAULT_DISPLAY, taskId = task2.taskId)
-    // Task is being minimized so mark it as not visible.
-    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")
-  }
-
-  @Test
-  fun moveFocusedTaskToDesktop_fullscreenTaskIsMovedToDesktop() {
-    val task1 = setUpFullscreenTask()
-    val task2 = setUpFullscreenTask()
-    val task3 = setUpFullscreenTask()
-
-    task1.isFocused = true
-    task2.isFocused = false
-    task3.isFocused = false
-
-    controller.moveFocusedTaskToDesktop(DEFAULT_DISPLAY, transitionSource = UNKNOWN)
-
-    val wct = getLatestEnterDesktopWct()
-    assertThat(wct.changes[task1.token.asBinder()]?.windowingMode)
-        .isEqualTo(WINDOWING_MODE_FREEFORM)
-  }
-
-  @Test
-  fun moveFocusedTaskToDesktop_splitScreenTaskIsMovedToDesktop() {
-    val task1 = setUpSplitScreenTask()
-    val task2 = setUpFullscreenTask()
-    val task3 = setUpFullscreenTask()
-    val task4 = setUpSplitScreenTask()
-
-    task1.isFocused = true
-    task2.isFocused = false
-    task3.isFocused = false
-    task4.isFocused = true
-
-    task4.parentTaskId = task1.taskId
-
-    controller.moveFocusedTaskToDesktop(DEFAULT_DISPLAY, transitionSource = UNKNOWN)
-
-    val wct = getLatestEnterDesktopWct()
-    assertThat(wct.changes[task4.token.asBinder()]?.windowingMode)
-        .isEqualTo(WINDOWING_MODE_FREEFORM)
-    verify(splitScreenController)
-        .prepareExitSplitScreen(any(), anyInt(), eq(SplitScreenController.EXIT_REASON_DESKTOP_MODE))
-  }
-
-  @Test
-  fun moveFocusedTaskToFullscreen() {
-    val task1 = setUpFreeformTask()
-    val task2 = setUpFreeformTask()
-    val task3 = setUpFreeformTask()
-
-    task1.isFocused = false
-    task2.isFocused = true
-    task3.isFocused = false
-
-    controller.enterFullscreen(DEFAULT_DISPLAY, transitionSource = UNKNOWN)
-
-    val wct = getLatestExitDesktopWct()
-    assertThat(wct.changes[task2.token.asBinder()]?.windowingMode)
-        .isEqualTo(WINDOWING_MODE_UNDEFINED) // inherited FULLSCREEN
-  }
-
-  @Test
-  fun moveFocusedTaskToFullscreen_onlyVisibleNonMinimizedTask_removesWallpaperActivity() {
-    val task1 = setUpFreeformTask()
-    val task2 = setUpFreeformTask()
-    val task3 = setUpFreeformTask()
-    val wallpaperToken = MockToken().token()
-
-    task1.isFocused = false
-    task2.isFocused = true
-    task3.isFocused = false
-    taskRepository.wallpaperActivityToken = wallpaperToken
-    taskRepository.minimizeTask(DEFAULT_DISPLAY, task1.taskId)
-    taskRepository.updateTask(DEFAULT_DISPLAY, task3.taskId, isVisible = false)
-
-    controller.enterFullscreen(DEFAULT_DISPLAY, transitionSource = UNKNOWN)
-
-    val wct = getLatestExitDesktopWct()
-    val taskChange = assertNotNull(wct.changes[task2.token.asBinder()])
-    assertThat(taskChange.windowingMode).isEqualTo(WINDOWING_MODE_UNDEFINED) // inherited FULLSCREEN
-    wct.assertRemoveAt(index = 0, wallpaperToken)
-  }
-
-  @Test
-  fun moveFocusedTaskToFullscreen_multipleVisibleTasks_doesNotRemoveWallpaperActivity() {
-    val task1 = setUpFreeformTask()
-    val task2 = setUpFreeformTask()
-    val task3 = setUpFreeformTask()
-    val wallpaperToken = MockToken().token()
-
-    task1.isFocused = false
-    task2.isFocused = true
-    task3.isFocused = false
-    taskRepository.wallpaperActivityToken = wallpaperToken
-    controller.enterFullscreen(DEFAULT_DISPLAY, transitionSource = UNKNOWN)
-
-    val wct = getLatestExitDesktopWct()
-    val taskChange = assertNotNull(wct.changes[task2.token.asBinder()])
-    assertThat(taskChange.windowingMode).isEqualTo(WINDOWING_MODE_UNDEFINED) // inherited FULLSCREEN
-    // Does not remove wallpaper activity, as desktop still has visible desktop tasks
-    assertThat(wct.hierarchyOps).isEmpty()
-  }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION)
-  fun removeDesktop_multipleTasks_removesAll() {
-    val task1 = setUpFreeformTask()
-    val task2 = setUpFreeformTask()
-    val task3 = setUpFreeformTask()
-    taskRepository.minimizeTask(DEFAULT_DISPLAY, task2.taskId)
-
-    controller.removeDesktop(displayId = DEFAULT_DISPLAY)
-
-    val wct = getLatestWct(TRANSIT_CLOSE)
-    assertThat(wct.hierarchyOps).hasSize(3)
-    wct.assertRemoveAt(index = 0, task1.token)
-    wct.assertRemoveAt(index = 1, task2.token)
-    wct.assertRemoveAt(index = 2, task3.token)
-  }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION)
-  fun removeDesktop_multipleTasksWithBackgroundTask_removesAll() {
-    val task1 = setUpFreeformTask()
-    val task2 = setUpFreeformTask()
-    val task3 = setUpFreeformTask()
-    taskRepository.minimizeTask(DEFAULT_DISPLAY, task2.taskId)
-    whenever(shellTaskOrganizer.getRunningTaskInfo(task3.taskId)).thenReturn(null)
-
-    controller.removeDesktop(displayId = DEFAULT_DISPLAY)
-
-    val wct = getLatestWct(TRANSIT_CLOSE)
-    assertThat(wct.hierarchyOps).hasSize(2)
-    wct.assertRemoveAt(index = 0, task1.token)
-    wct.assertRemoveAt(index = 1, task2.token)
-    verify(recentTasksController).removeBackgroundTask(task3.taskId)
-  }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
-  fun dragToDesktop_landscapeDevice_resizable_undefinedOrientation_defaultLandscapeBounds() {
-    val spyController = spy(controller)
-    whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
-    whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull()))
-        .thenReturn(DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR)
-
-    val task = setUpFullscreenTask()
-    setUpLandscapeDisplay()
-
-    spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task, mockSurface)
-    val wct = getLatestDragToDesktopWct()
-    assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_LANDSCAPE_BOUNDS)
-  }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
-  fun dragToDesktop_landscapeDevice_resizable_landscapeOrientation_defaultLandscapeBounds() {
-    val spyController = spy(controller)
-    whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
-    whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull()))
-        .thenReturn(DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR)
-
-    val task = setUpFullscreenTask(screenOrientation = SCREEN_ORIENTATION_LANDSCAPE)
-    setUpLandscapeDisplay()
-
-    spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task, mockSurface)
-    val wct = getLatestDragToDesktopWct()
-    assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_LANDSCAPE_BOUNDS)
-  }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
-  fun dragToDesktop_landscapeDevice_resizable_portraitOrientation_resizablePortraitBounds() {
-    val spyController = spy(controller)
-    whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
-    whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull()))
-        .thenReturn(DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR)
-
-    val task =
-        setUpFullscreenTask(screenOrientation = SCREEN_ORIENTATION_PORTRAIT, shouldLetterbox = true)
-    setUpLandscapeDisplay()
-
-    spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task, mockSurface)
-    val wct = getLatestDragToDesktopWct()
-    assertThat(findBoundsChange(wct, task)).isEqualTo(RESIZABLE_PORTRAIT_BOUNDS)
-  }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
-  fun dragToDesktop_landscapeDevice_unResizable_landscapeOrientation_defaultLandscapeBounds() {
-    val spyController = spy(controller)
-    whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
-    whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull()))
-        .thenReturn(DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR)
-
-    val task =
-        setUpFullscreenTask(isResizable = false, screenOrientation = SCREEN_ORIENTATION_LANDSCAPE)
-    setUpLandscapeDisplay()
-
-    spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task, mockSurface)
-    val wct = getLatestDragToDesktopWct()
-    assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_LANDSCAPE_BOUNDS)
-  }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
-  fun dragToDesktop_landscapeDevice_unResizable_portraitOrientation_unResizablePortraitBounds() {
-    val spyController = spy(controller)
-    whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
-    whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull()))
-        .thenReturn(DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR)
-
-    val task =
-        setUpFullscreenTask(
-            isResizable = false,
-            screenOrientation = SCREEN_ORIENTATION_PORTRAIT,
-            shouldLetterbox = true)
-    setUpLandscapeDisplay()
-
-    spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task, mockSurface)
-    val wct = getLatestDragToDesktopWct()
-    assertThat(findBoundsChange(wct, task)).isEqualTo(UNRESIZABLE_PORTRAIT_BOUNDS)
-  }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
-  fun dragToDesktop_portraitDevice_resizable_undefinedOrientation_defaultPortraitBounds() {
-    val spyController = spy(controller)
-    whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
-    whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull()))
-        .thenReturn(DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR)
-
-    val task = setUpFullscreenTask(deviceOrientation = ORIENTATION_PORTRAIT)
-    setUpPortraitDisplay()
-
-    spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task, mockSurface)
-    val wct = getLatestDragToDesktopWct()
-    assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_PORTRAIT_BOUNDS)
-  }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
-  fun dragToDesktop_portraitDevice_resizable_portraitOrientation_defaultPortraitBounds() {
-    val spyController = spy(controller)
-    whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
-    whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull()))
-        .thenReturn(DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR)
-
-    val task =
-        setUpFullscreenTask(
-            deviceOrientation = ORIENTATION_PORTRAIT,
-            screenOrientation = SCREEN_ORIENTATION_PORTRAIT)
-    setUpPortraitDisplay()
-
-    spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task, mockSurface)
-    val wct = getLatestDragToDesktopWct()
-    assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_PORTRAIT_BOUNDS)
-  }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
-  fun dragToDesktop_portraitDevice_resizable_landscapeOrientation_resizableLandscapeBounds() {
-    val spyController = spy(controller)
-    whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
-    whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull()))
-        .thenReturn(DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR)
-
-    val task =
-        setUpFullscreenTask(
-            deviceOrientation = ORIENTATION_PORTRAIT,
-            screenOrientation = SCREEN_ORIENTATION_LANDSCAPE,
-            shouldLetterbox = true)
-    setUpPortraitDisplay()
-
-    spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task, mockSurface)
-    val wct = getLatestDragToDesktopWct()
-    assertThat(findBoundsChange(wct, task)).isEqualTo(RESIZABLE_LANDSCAPE_BOUNDS)
-  }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
-  fun dragToDesktop_portraitDevice_unResizable_portraitOrientation_defaultPortraitBounds() {
-    val spyController = spy(controller)
-    whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
-    whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull()))
-        .thenReturn(DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR)
-
-    val task =
-        setUpFullscreenTask(
-            isResizable = false,
-            deviceOrientation = ORIENTATION_PORTRAIT,
-            screenOrientation = SCREEN_ORIENTATION_PORTRAIT)
-    setUpPortraitDisplay()
-
-    spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task, mockSurface)
-    val wct = getLatestDragToDesktopWct()
-    assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_PORTRAIT_BOUNDS)
-  }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
-  fun dragToDesktop_portraitDevice_unResizable_landscapeOrientation_unResizableLandscapeBounds() {
-    val spyController = spy(controller)
-    whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
-    whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull()))
-        .thenReturn(DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR)
-
-    val task =
-        setUpFullscreenTask(
-            isResizable = false,
-            deviceOrientation = ORIENTATION_PORTRAIT,
-            screenOrientation = SCREEN_ORIENTATION_LANDSCAPE,
-            shouldLetterbox = true)
-    setUpPortraitDisplay()
-
-    spyController.onDragPositioningEndThroughStatusBar(PointF(200f, 200f), task, mockSurface)
-    val wct = getLatestDragToDesktopWct()
-    assertThat(findBoundsChange(wct, task)).isEqualTo(UNRESIZABLE_LANDSCAPE_BOUNDS)
-  }
-
-  @Test
-  fun onDesktopDragMove_endsOutsideValidDragArea_snapsToValidBounds() {
-    val task = setUpFreeformTask()
-    val spyController = spy(controller)
-    val mockSurface = mock(SurfaceControl::class.java)
-    val mockDisplayLayout = mock(DisplayLayout::class.java)
-    whenever(displayController.getDisplayLayout(task.displayId)).thenReturn(mockDisplayLayout)
-    whenever(mockDisplayLayout.stableInsets()).thenReturn(Rect(0, 100, 2000, 2000))
-    spyController.onDragPositioningMove(task, mockSurface, 200f, Rect(100, -100, 500, 1000))
-
-    whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
-    whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull()))
-      .thenReturn(DesktopModeVisualIndicator.IndicatorType.NO_INDICATOR)
-    spyController.onDragPositioningEnd(
-        task,
-        mockSurface,
-        Point(100, -100), /* position */
-        PointF(200f, -200f), /* inputCoordinate */
-        Rect(100, -100, 500, 1000), /* currentDragBounds */
-        Rect(0, 50, 2000, 2000), /* validDragArea */
-        Rect() /* dragStartBounds */,
-        motionEvent,
-        desktopWindowDecoration,
-        )
-    val rectAfterEnd = Rect(100, 50, 500, 1150)
-    verify(transitions)
-        .startTransition(
-            eq(TRANSIT_CHANGE),
-            Mockito.argThat { wct ->
-              return@argThat wct.changes.any { (token, change) ->
-                change.configuration.windowConfiguration.bounds == rectAfterEnd
-              }
-            },
-            eq(null))
-  }
-
-  @Test
-  fun onDesktopDragEnd_noIndicator_updatesTaskBounds() {
-    val task = setUpFreeformTask()
-    val spyController = spy(controller)
-    val mockSurface = mock(SurfaceControl::class.java)
-    val mockDisplayLayout = mock(DisplayLayout::class.java)
-    whenever(displayController.getDisplayLayout(task.displayId)).thenReturn(mockDisplayLayout)
-    whenever(mockDisplayLayout.stableInsets()).thenReturn(Rect(0, 100, 2000, 2000))
-    spyController.onDragPositioningMove(task, mockSurface, 200f, Rect(100, 200, 500, 1000))
-
-    val currentDragBounds = Rect(100, 200, 500, 1000)
-    whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
-    whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull()))
-      .thenReturn(DesktopModeVisualIndicator.IndicatorType.NO_INDICATOR)
-
-    spyController.onDragPositioningEnd(
-      task,
-      mockSurface,
-      Point(100, 200), /* position */
-      PointF(200f, 300f), /* inputCoordinate */
-      currentDragBounds, /* currentDragBounds */
-      Rect(0, 50, 2000, 2000) /* validDragArea */,
-      Rect() /* dragStartBounds */,
-      motionEvent,
-      desktopWindowDecoration,
-      )
-
-
-    verify(transitions)
-      .startTransition(
-        eq(TRANSIT_CHANGE),
-        Mockito.argThat { wct ->
-          return@argThat wct.changes.any { (token, change) ->
-            change.configuration.windowConfiguration.bounds == currentDragBounds
-          }
-        },
-        eq(null))
-  }
-
-  @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
-    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,
-      Rect(0, 50, 2000, 2000) /* validDragArea */,
-      Rect() /* dragStartBounds */,
-      motionEvent,
-      desktopWindowDecoration)
-
-    // Assert bounds set to stable bounds
-    val wct = getLatestToggleResizeDesktopTaskWct(currentDragBounds)
-    assertThat(findBoundsChange(wct, task)).isEqualTo(STABLE_BOUNDS)
-    // Assert event is properly logged
-    verify(desktopModeEventLogger, times(1)).logTaskResizingStarted(
-      ResizeTrigger.DRAG_TO_TOP_RESIZE_TRIGGER,
-      InputMethod.UNKNOWN_INPUT_METHOD,
-      task,
-      task.configuration.windowConfiguration.bounds.width(),
-      task.configuration.windowConfiguration.bounds.height(),
-      displayController
-    )
-    verify(desktopModeEventLogger, times(1)).logTaskResizingEnded(
-      ResizeTrigger.DRAG_TO_TOP_RESIZE_TRIGGER,
-      InputMethod.UNKNOWN_INPUT_METHOD,
-      task,
-      STABLE_BOUNDS.width(),
-      STABLE_BOUNDS.height(),
-      displayController
-    )
-  }
-
-  @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(),
-    )
-    // Assert no event is logged
-    verify(desktopModeEventLogger, never()).logTaskResizingStarted(
-      any(), any(), any(), any(), any(), any(), any()
-    )
-    verify(desktopModeEventLogger, never()).logTaskResizingEnded(
-      any(), any(), any(), any(), any(), any(), any()
-    )
-  }
-
-  @Test
-  fun enterSplit_freeformTaskIsMovedToSplit() {
-    val task1 = setUpFreeformTask()
-    val task2 = setUpFreeformTask()
-    val task3 = setUpFreeformTask()
-
-    task1.isFocused = false
-    task2.isFocused = true
-    task3.isFocused = false
-
-    controller.enterSplit(DEFAULT_DISPLAY, leftOrTop = false)
-
-    verify(splitScreenController)
-        .requestEnterSplitSelect(
-            eq(task2),
-            any(),
-            eq(SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT),
-            eq(task2.configuration.windowConfiguration.bounds))
-  }
-
-  @Test
-  fun enterSplit_onlyVisibleNonMinimizedTask_removesWallpaperActivity() {
-    val task1 = setUpFreeformTask()
-    val task2 = setUpFreeformTask()
-    val task3 = setUpFreeformTask()
-    val wallpaperToken = MockToken().token()
-
-    task1.isFocused = false
-    task2.isFocused = true
-    task3.isFocused = false
-    taskRepository.wallpaperActivityToken = wallpaperToken
-    taskRepository.minimizeTask(DEFAULT_DISPLAY, task1.taskId)
-    taskRepository.updateTask(DEFAULT_DISPLAY, task3.taskId, isVisible = false)
-
-    controller.enterSplit(DEFAULT_DISPLAY, leftOrTop = false)
-
-    val wctArgument = ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
-    verify(splitScreenController)
-      .requestEnterSplitSelect(
-        eq(task2),
-        wctArgument.capture(),
-        eq(SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT),
-        eq(task2.configuration.windowConfiguration.bounds))
-    // Removes wallpaper activity when leaving desktop
-    wctArgument.value.assertRemoveAt(index = 0, wallpaperToken)
-  }
-
-  @Test
-  fun enterSplit_multipleVisibleNonMinimizedTasks_removesWallpaperActivity() {
-    val task1 = setUpFreeformTask()
-    val task2 = setUpFreeformTask()
-    val task3 = setUpFreeformTask()
-    val wallpaperToken = MockToken().token()
-
-    task1.isFocused = false
-    task2.isFocused = true
-    task3.isFocused = false
-    taskRepository.wallpaperActivityToken = wallpaperToken
-
-    controller.enterSplit(DEFAULT_DISPLAY, leftOrTop = false)
-
-    val wctArgument = ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
-    verify(splitScreenController)
-      .requestEnterSplitSelect(
-        eq(task2),
-        wctArgument.capture(),
-        eq(SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT),
-        eq(task2.configuration.windowConfiguration.bounds))
-    // Does not remove wallpaper activity, as desktop still has visible desktop tasks
-    assertThat(wctArgument.value.hierarchyOps).isEmpty()
-  }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MULTI_INSTANCE_FEATURES)
-  fun newWindow_fromFullscreenOpensInSplit() {
-    setUpLandscapeDisplay()
-    val task = setUpFullscreenTask()
-    val optionsCaptor = ArgumentCaptor.forClass(Bundle::class.java)
-    runOpenNewWindow(task)
-    verify(splitScreenController)
-      .startIntent(any(), anyInt(), any(), any(),
-        optionsCaptor.capture(), anyOrNull(), eq(true), eq(SPLIT_INDEX_UNDEFINED)
-      )
-    assertThat(ActivityOptions.fromBundle(optionsCaptor.value).launchWindowingMode)
-      .isEqualTo(WINDOWING_MODE_MULTI_WINDOW)
-  }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MULTI_INSTANCE_FEATURES)
-  fun newWindow_fromSplitOpensInSplit() {
-    setUpLandscapeDisplay()
-    val task = setUpSplitScreenTask()
-    val optionsCaptor = ArgumentCaptor.forClass(Bundle::class.java)
-    runOpenNewWindow(task)
-    verify(splitScreenController)
-      .startIntent(
-        any(), anyInt(), any(), any(),
-        optionsCaptor.capture(), anyOrNull(), eq(true), eq(SPLIT_INDEX_UNDEFINED)
-      )
-    assertThat(ActivityOptions.fromBundle(optionsCaptor.value).launchWindowingMode)
-      .isEqualTo(WINDOWING_MODE_MULTI_WINDOW)
-  }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MULTI_INSTANCE_FEATURES)
-  fun newWindow_fromFreeformAddsNewWindow() {
-    setUpLandscapeDisplay()
-    val task = setUpFreeformTask()
-    val wctCaptor = argumentCaptor<WindowContainerTransaction>()
-    val transition = Binder()
-    whenever(mMockDesktopImmersiveController
-      .exitImmersiveIfApplicable(any(), anyInt(), anyOrNull(), any()))
-      .thenReturn(ExitResult.NoExit)
-    whenever(desktopMixedTransitionHandler
-      .startLaunchTransition(anyInt(), any(), anyOrNull(), anyOrNull(), anyOrNull()))
-      .thenReturn(transition)
-
-    runOpenNewWindow(task)
-
-    verify(desktopMixedTransitionHandler)
-      .startLaunchTransition(anyInt(), wctCaptor.capture(), anyOrNull(), anyOrNull(), anyOrNull())
-    assertThat(ActivityOptions.fromBundle(wctCaptor.firstValue.hierarchyOps[0].launchOptions)
-      .launchWindowingMode).isEqualTo(WINDOWING_MODE_FREEFORM)
-  }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MULTI_INSTANCE_FEATURES)
-  fun newWindow_fromFreeform_exitsImmersiveIfNeeded() {
-    setUpLandscapeDisplay()
-    val immersiveTask = setUpFreeformTask()
-    val task = setUpFreeformTask()
-    val runOnStart = RunOnStartTransitionCallback()
-    val transition = Binder()
-    whenever(mMockDesktopImmersiveController
-      .exitImmersiveIfApplicable(any(), anyInt(), anyOrNull(), any()))
-      .thenReturn(ExitResult.Exit(immersiveTask.taskId, runOnStart))
-    whenever(desktopMixedTransitionHandler
-      .startLaunchTransition(anyInt(), any(), anyOrNull(), anyOrNull(), anyOrNull()))
-      .thenReturn(transition)
-
-    runOpenNewWindow(task)
-
-    runOnStart.assertOnlyInvocation(transition)
-  }
-
-  private fun runOpenNewWindow(task: RunningTaskInfo) {
-    markTaskVisible(task)
-    task.baseActivity = mock(ComponentName::class.java)
-    task.isFocused = true
-    runningTasks.add(task)
-    controller.openNewWindow(task)
-  }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MULTI_INSTANCE_FEATURES)
-  fun openInstance_fromFullscreenOpensInSplit() {
-    setUpLandscapeDisplay()
-    val task = setUpFullscreenTask()
-    val taskToRequest = setUpFreeformTask()
-    val optionsCaptor = ArgumentCaptor.forClass(Bundle::class.java)
-    runOpenInstance(task, taskToRequest.taskId)
-    verify(splitScreenController)
-      .startTask(anyInt(), anyInt(), optionsCaptor.capture(), anyOrNull())
-    assertThat(ActivityOptions.fromBundle(optionsCaptor.value).launchWindowingMode)
-      .isEqualTo(WINDOWING_MODE_MULTI_WINDOW)
-  }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MULTI_INSTANCE_FEATURES)
-  fun openInstance_fromSplitOpensInSplit() {
-    setUpLandscapeDisplay()
-    val task = setUpSplitScreenTask()
-    val taskToRequest = setUpFreeformTask()
-    val optionsCaptor = ArgumentCaptor.forClass(Bundle::class.java)
-    runOpenInstance(task, taskToRequest.taskId)
-    verify(splitScreenController)
-      .startTask(anyInt(), anyInt(), optionsCaptor.capture(), anyOrNull())
-    assertThat(ActivityOptions.fromBundle(optionsCaptor.value).launchWindowingMode)
-      .isEqualTo(WINDOWING_MODE_MULTI_WINDOW)
-  }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MULTI_INSTANCE_FEATURES)
-  fun openInstance_fromFreeformAddsNewWindow() {
-    setUpLandscapeDisplay()
-    val task = setUpFreeformTask()
-    val taskToRequest = setUpFreeformTask()
-    runOpenInstance(task, taskToRequest.taskId)
-    verify(desktopMixedTransitionHandler).startLaunchTransition(anyInt(), any(), anyInt(),
-      anyOrNull(), anyOrNull())
-    val wct = getLatestDesktopMixedTaskWct(type = TRANSIT_TO_FRONT)
-    assertThat(wct.hierarchyOps).hasSize(1)
-    wct.assertReorderAt(index = 0, taskToRequest)
-  }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MULTI_INSTANCE_FEATURES)
-  fun openInstance_fromFreeform_minimizesIfNeeded() {
-    setUpLandscapeDisplay()
-    val freeformTasks = (1..MAX_TASK_LIMIT + 1).map { _ -> setUpFreeformTask() }
-    val oldestTask = freeformTasks.first()
-    val newestTask = freeformTasks.last()
-
-    val transition = Binder()
-    val wctCaptor = argumentCaptor<WindowContainerTransaction>()
-    whenever(desktopMixedTransitionHandler.startLaunchTransition(anyInt(), wctCaptor.capture(),
-      anyInt(), anyOrNull(), anyOrNull()
-    ))
-      .thenReturn(transition)
-
-    runOpenInstance(newestTask, freeformTasks[1].taskId)
-
-    val wct = wctCaptor.firstValue
-    assertThat(wct.hierarchyOps.size).isEqualTo(2) // move-to-front + minimize
-    wct.assertReorderAt(0, freeformTasks[1], toTop = true)
-    wct.assertReorderAt(1, oldestTask, toTop = false)
-  }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MULTI_INSTANCE_FEATURES)
-  fun openInstance_fromFreeform_exitsImmersiveIfNeeded() {
-    setUpLandscapeDisplay()
-    val freeformTask = setUpFreeformTask()
-    val immersiveTask = setUpFreeformTask()
-    taskRepository.setTaskInFullImmersiveState(
-      displayId = immersiveTask.displayId,
-      taskId = immersiveTask.taskId,
-      immersive = true
-    )
-    val runOnStartTransit = RunOnStartTransitionCallback()
-    val transition = Binder()
-    whenever(desktopMixedTransitionHandler.startLaunchTransition(anyInt(), any(), anyInt(),
-      anyOrNull(), anyOrNull()
-    ))
-      .thenReturn(transition)
-    whenever(mMockDesktopImmersiveController
-      .exitImmersiveIfApplicable(
-        any(), eq(DEFAULT_DISPLAY), eq(freeformTask.taskId), any()))
-      .thenReturn(
-        ExitResult.Exit(
-        exitingTask = immersiveTask.taskId,
-        runOnTransitionStart = runOnStartTransit,
-      ))
-
-    runOpenInstance(immersiveTask, freeformTask.taskId)
-
-    verify(mMockDesktopImmersiveController)
-      .exitImmersiveIfApplicable(any(), eq(immersiveTask.displayId), eq(freeformTask.taskId), any())
-    runOnStartTransit.assertOnlyInvocation(transition)
-  }
-
-  private fun runOpenInstance(
-    callingTask: RunningTaskInfo,
-    requestedTaskId: Int
-  ) {
-    markTaskVisible(callingTask)
-    callingTask.baseActivity = mock(ComponentName::class.java)
-    callingTask.isFocused = true
-    runningTasks.add(callingTask)
-    controller.openInstance(callingTask, requestedTaskId)
-  }
-
-  @Test
-  fun toggleBounds_togglesToStableBounds() {
-    val bounds = Rect(0, 0, 100, 100)
-    val task = setUpFreeformTask(DEFAULT_DISPLAY, bounds)
-
-    controller.toggleDesktopTaskSize(
-      task,
-      ToggleTaskSizeInteraction(
-        ToggleTaskSizeInteraction.Direction.MAXIMIZE,
-        ToggleTaskSizeInteraction.Source.HEADER_BUTTON_TO_MAXIMIZE,
-        InputMethod.TOUCH
-      )
-    )
-
-    // Assert bounds set to stable bounds
-    val wct = getLatestToggleResizeDesktopTaskWct()
-    assertThat(findBoundsChange(wct, task)).isEqualTo(STABLE_BOUNDS)
-    verify(desktopModeEventLogger, times(1)).logTaskResizingEnded(
-      ResizeTrigger.MAXIMIZE_BUTTON,
-      InputMethod.TOUCH,
-      task,
-      STABLE_BOUNDS.width(),
-      STABLE_BOUNDS.height(),
-      displayController
-    )
-  }
-
-  @Test
-  @DisableFlags(Flags.FLAG_ENABLE_TILE_RESIZING)
-  fun snapToHalfScreen_getSnapBounds_calculatesBoundsForResizable() {
-    val bounds = Rect(100, 100, 300, 300)
-    val task = setUpFreeformTask(DEFAULT_DISPLAY, bounds).apply {
-      topActivityInfo = ActivityInfo().apply {
-        screenOrientation = SCREEN_ORIENTATION_LANDSCAPE
-        configuration.windowConfiguration.appBounds = bounds
-      }
-      isResizeable = true
-    }
-
-    val currentDragBounds = Rect(0, 100, 200, 300)
-    val expectedBounds = Rect(
-      STABLE_BOUNDS.left, STABLE_BOUNDS.top, STABLE_BOUNDS.right / 2, STABLE_BOUNDS.bottom
-    )
-
-    controller.snapToHalfScreen(task, mockSurface, currentDragBounds, SnapPosition.LEFT,
-      ResizeTrigger.SNAP_LEFT_MENU, InputMethod.TOUCH, desktopWindowDecoration)
-    // Assert bounds set to stable bounds
-    val wct = getLatestToggleResizeDesktopTaskWct(currentDragBounds)
-    assertThat(findBoundsChange(wct, task)).isEqualTo(expectedBounds)
-    verify(desktopModeEventLogger, times(1)).logTaskResizingEnded(
-      ResizeTrigger.SNAP_LEFT_MENU,
-      InputMethod.TOUCH,
-      task,
-      expectedBounds.width(),
-      expectedBounds.height(),
-      displayController
-    )
-  }
-
-  @Test
-  @DisableFlags(Flags.FLAG_ENABLE_TILE_RESIZING)
-  fun snapToHalfScreen_snapBoundsWhenAlreadySnapped_animatesSurfaceWithoutWCT() {
-    // Set up task to already be in snapped-left bounds
-    val bounds = Rect(
-      STABLE_BOUNDS.left, STABLE_BOUNDS.top, STABLE_BOUNDS.right / 2, STABLE_BOUNDS.bottom
-    )
-    val task = setUpFreeformTask(DEFAULT_DISPLAY, bounds).apply {
-      topActivityInfo = ActivityInfo().apply {
-        screenOrientation = SCREEN_ORIENTATION_LANDSCAPE
-        configuration.windowConfiguration.appBounds = bounds
-      }
-      isResizeable = true
-    }
-
-    // Attempt to snap left again
-    val currentDragBounds = Rect(bounds).apply { offset(-100, 0) }
-    controller.snapToHalfScreen(task, mockSurface, currentDragBounds, SnapPosition.LEFT,
-      ResizeTrigger.SNAP_LEFT_MENU, InputMethod.TOUCH, 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(bounds),
-      anyOrNull(),
-    )
-    verify(desktopModeEventLogger, times(1)).logTaskResizingEnded(
-      ResizeTrigger.SNAP_LEFT_MENU,
-      InputMethod.TOUCH,
-      task,
-      bounds.width(),
-      bounds.height(),
-      displayController
-    )
-  }
-
-  @Test
-  @DisableFlags(Flags.FLAG_DISABLE_NON_RESIZABLE_APP_SNAP_RESIZING, Flags.FLAG_ENABLE_TILE_RESIZING)
-  fun handleSnapResizingTaskOnDrag_nonResizable_snapsToHalfScreen() {
-    val task = setUpFreeformTask(DEFAULT_DISPLAY, Rect(0, 0, 200, 100)).apply {
-      isResizeable = false
-    }
-    val preDragBounds = Rect(100, 100, 400, 500)
-    val currentDragBounds = Rect(0, 100, 300, 500)
-    val expectedBounds =
-      Rect(STABLE_BOUNDS.left, STABLE_BOUNDS.top, STABLE_BOUNDS.right / 2, STABLE_BOUNDS.bottom)
-
-    controller.handleSnapResizingTaskOnDrag(
-
-      task, SnapPosition.LEFT, mockSurface, currentDragBounds, preDragBounds, motionEvent,
-      desktopWindowDecoration
-    )
-    val wct = getLatestToggleResizeDesktopTaskWct(currentDragBounds)
-    assertThat(findBoundsChange(wct, task)).isEqualTo(
-      expectedBounds
-    )
-    verify(desktopModeEventLogger, times(1)).logTaskResizingStarted(
-      ResizeTrigger.DRAG_LEFT,
-      InputMethod.UNKNOWN_INPUT_METHOD,
-      task,
-      preDragBounds.width(),
-      preDragBounds.height(),
-      displayController
-    )
-  }
-
-  @Test
-  @EnableFlags(Flags.FLAG_DISABLE_NON_RESIZABLE_APP_SNAP_RESIZING)
-  fun handleSnapResizingTaskOnDrag_nonResizable_startsRepositionAnimation() {
-    val task = setUpFreeformTask(DEFAULT_DISPLAY, Rect(0, 0, 200, 100)).apply {
-      isResizeable = false
-    }
-    val preDragBounds = Rect(100, 100, 400, 500)
-    val currentDragBounds = Rect(0, 100, 300, 500)
-
-    controller.handleSnapResizingTaskOnDrag(
-      task, SnapPosition.LEFT, mockSurface, currentDragBounds, preDragBounds, motionEvent,
-      desktopWindowDecoration)
-    verify(mReturnToDragStartAnimator).start(
-      eq(task.taskId),
-      eq(mockSurface),
-      eq(currentDragBounds),
-      eq(preDragBounds),
-      any(),
-    )
-    verify(desktopModeEventLogger, never()).logTaskResizingStarted(
-      any(),
-      any(),
-      any(),
-      any(),
-      any(),
-      any(),
-      any()
-    )
-  }
-
-  @Test
-  @EnableFlags(
-    Flags.FLAG_DISABLE_NON_RESIZABLE_APP_SNAP_RESIZING
-  )
-  fun handleInstantSnapResizingTask_nonResizable_animatorNotStartedAndShowsToast() {
-    val taskBounds = Rect(0, 0, 200, 100)
-    val task = setUpFreeformTask(DEFAULT_DISPLAY, taskBounds).apply {
-      isResizeable = false
-    }
-
-    controller.handleInstantSnapResizingTask(
-      task,
-      SnapPosition.LEFT,
-      ResizeTrigger.SNAP_LEFT_MENU,
-      InputMethod.MOUSE,
-      desktopWindowDecoration
-    )
-
-    // Assert that task is NOT updated via WCT
-    verify(toggleResizeDesktopTaskTransitionHandler, never()).startTransition(any(), any())
-    verify(mockToast).show()
-  }
-
-  @Test
-  @EnableFlags(Flags.FLAG_DISABLE_NON_RESIZABLE_APP_SNAP_RESIZING)
-  @DisableFlags(Flags.FLAG_ENABLE_TILE_RESIZING)
-  fun handleInstantSnapResizingTask_resizable_snapsToHalfScreenAndNotShowToast() {
-    val taskBounds = Rect(0, 0, 200, 100)
-    val task = setUpFreeformTask(DEFAULT_DISPLAY, taskBounds).apply {
-      isResizeable = true
-    }
-    val expectedBounds = Rect(
-      STABLE_BOUNDS.left, STABLE_BOUNDS.top, STABLE_BOUNDS.right / 2, STABLE_BOUNDS.bottom
-    )
-
-    controller.handleInstantSnapResizingTask(
-      task, SnapPosition.LEFT, ResizeTrigger.SNAP_LEFT_MENU, InputMethod.MOUSE,
-      desktopWindowDecoration
-    )
-
-    // Assert bounds set to half of the stable bounds
-    val wct = getLatestToggleResizeDesktopTaskWct(taskBounds)
-    assertThat(findBoundsChange(wct, task)).isEqualTo(expectedBounds)
-    verify(mockToast, never()).show()
-    verify(desktopModeEventLogger, times(1)).logTaskResizingStarted(
-      ResizeTrigger.SNAP_LEFT_MENU,
-      InputMethod.MOUSE,
-      task,
-      taskBounds.width(),
-      taskBounds.height(),
-      displayController
-    )
-    verify(desktopModeEventLogger, times(1)).logTaskResizingEnded(
-      ResizeTrigger.SNAP_LEFT_MENU,
-      InputMethod.MOUSE,
-      task,
-      expectedBounds.width(),
-      expectedBounds.height(),
-      displayController
-    )
-  }
-
-  @Test
-  fun toggleBounds_togglesToCalculatedBoundsForNonResizable() {
-    val bounds = Rect(0, 0, 200, 100)
-    val task = setUpFreeformTask(DEFAULT_DISPLAY, bounds).apply {
-      topActivityInfo = ActivityInfo().apply {
-        screenOrientation = SCREEN_ORIENTATION_LANDSCAPE
-        configuration.windowConfiguration.appBounds = bounds
-      }
-      appCompatTaskInfo.topActivityLetterboxAppWidth = bounds.width()
-      appCompatTaskInfo.topActivityLetterboxAppHeight = bounds.height()
-      isResizeable = false
-    }
-
-    // Bounds should be 1000 x 500, vertically centered in the 1000 x 1000 stable bounds
-    val expectedBounds = Rect(STABLE_BOUNDS.left, 250, STABLE_BOUNDS.right, 750)
-
-    controller.toggleDesktopTaskSize(
-      task,
-      ToggleTaskSizeInteraction(
-        ToggleTaskSizeInteraction.Direction.MAXIMIZE,
-        ToggleTaskSizeInteraction.Source.HEADER_BUTTON_TO_MAXIMIZE,
-        InputMethod.TOUCH
-      )
-    )
-
-    // Assert bounds set to stable bounds
-    val wct = getLatestToggleResizeDesktopTaskWct()
-    assertThat(findBoundsChange(wct, task)).isEqualTo(expectedBounds)
-    verify(desktopModeEventLogger, times(1)).logTaskResizingEnded(
-      ResizeTrigger.MAXIMIZE_BUTTON,
-      InputMethod.TOUCH,
-      task,
-      expectedBounds.width(),
-      expectedBounds.height(),
-      displayController
-    )
-  }
-
-  @Test
-  fun toggleBounds_lastBoundsBeforeMaximizeSaved() {
-    val bounds = Rect(0, 0, 100, 100)
-    val task = setUpFreeformTask(DEFAULT_DISPLAY, bounds)
-
-    controller.toggleDesktopTaskSize(
-      task,
-      ToggleTaskSizeInteraction(
-        ToggleTaskSizeInteraction.Direction.MAXIMIZE,
-        ToggleTaskSizeInteraction.Source.HEADER_BUTTON_TO_MAXIMIZE,
-        InputMethod.TOUCH
-      )
-    )
-
-    assertThat(taskRepository.removeBoundsBeforeMaximize(task.taskId)).isEqualTo(bounds)
-    verify(desktopModeEventLogger, never()).logTaskResizingEnded(
-      any(), any(), any(), any(),
-      any(), any(), any()
-    )
-  }
-
-  @Test
-  fun toggleBounds_togglesFromStableBoundsToLastBoundsBeforeMaximize() {
-    val boundsBeforeMaximize = Rect(0, 0, 100, 100)
-    val task = setUpFreeformTask(DEFAULT_DISPLAY, boundsBeforeMaximize)
-
-    // Maximize
-    controller.toggleDesktopTaskSize(
-      task,
-      ToggleTaskSizeInteraction(
-        ToggleTaskSizeInteraction.Direction.MAXIMIZE,
-        ToggleTaskSizeInteraction.Source.HEADER_BUTTON_TO_MAXIMIZE,
-        InputMethod.TOUCH
-      )
-    )
-    task.configuration.windowConfiguration.bounds.set(STABLE_BOUNDS)
-
-    // Restore
-    controller.toggleDesktopTaskSize(
-      task,
-      ToggleTaskSizeInteraction(
-        ToggleTaskSizeInteraction.Direction.RESTORE,
-        ToggleTaskSizeInteraction.Source.HEADER_BUTTON_TO_RESTORE,
-        InputMethod.TOUCH
-      )
-    )
-
-    // Assert bounds set to last bounds before maximize
-    val wct = getLatestToggleResizeDesktopTaskWct()
-    assertThat(findBoundsChange(wct, task)).isEqualTo(boundsBeforeMaximize)
-    verify(desktopModeEventLogger, times(1)).logTaskResizingEnded(
-      ResizeTrigger.MAXIMIZE_BUTTON,
-      InputMethod.TOUCH,
-      task,
-      boundsBeforeMaximize.width(),
-      boundsBeforeMaximize.height(),
-      displayController
-    )
-  }
-
-  @Test
-  fun toggleBounds_togglesFromStableBoundsToLastBoundsBeforeMaximize_nonResizeableEqualWidth() {
-    val boundsBeforeMaximize = Rect(0, 0, 100, 100)
-    val task = setUpFreeformTask(DEFAULT_DISPLAY, boundsBeforeMaximize).apply {
-      isResizeable = false
-    }
-
-    // Maximize
-    controller.toggleDesktopTaskSize(
-      task,
-      ToggleTaskSizeInteraction(
-        ToggleTaskSizeInteraction.Direction.MAXIMIZE,
-        ToggleTaskSizeInteraction.Source.HEADER_BUTTON_TO_MAXIMIZE,
-        InputMethod.TOUCH
-      )
-    )
-    task.configuration.windowConfiguration.bounds.set(STABLE_BOUNDS.left,
-      boundsBeforeMaximize.top, STABLE_BOUNDS.right, boundsBeforeMaximize.bottom)
-
-    // Restore
-    controller.toggleDesktopTaskSize(
-      task,
-      ToggleTaskSizeInteraction(
-        ToggleTaskSizeInteraction.Direction.RESTORE,
-        ToggleTaskSizeInteraction.Source.HEADER_BUTTON_TO_RESTORE,
-        InputMethod.TOUCH
-      )
-    )
-
-    // Assert bounds set to last bounds before maximize
-    val wct = getLatestToggleResizeDesktopTaskWct()
-    assertThat(findBoundsChange(wct, task)).isEqualTo(boundsBeforeMaximize)
-    verify(desktopModeEventLogger, times(1)).logTaskResizingEnded(
-      ResizeTrigger.MAXIMIZE_BUTTON,
-      InputMethod.TOUCH,
-      task,
-      boundsBeforeMaximize.width(),
-      boundsBeforeMaximize.height(),
-      displayController
-    )
-  }
-
-  @Test
-  fun toggleBounds_togglesFromStableBoundsToLastBoundsBeforeMaximize_nonResizeableEqualHeight() {
-    val boundsBeforeMaximize = Rect(0, 0, 100, 100)
-    val task = setUpFreeformTask(DEFAULT_DISPLAY, boundsBeforeMaximize).apply {
-      isResizeable = false
-    }
-
-    // Maximize
-    controller.toggleDesktopTaskSize(
-      task,
-      ToggleTaskSizeInteraction(
-        ToggleTaskSizeInteraction.Direction.MAXIMIZE,
-        ToggleTaskSizeInteraction.Source.HEADER_BUTTON_TO_MAXIMIZE,
-        InputMethod.TOUCH
-      )
-    )
-    task.configuration.windowConfiguration.bounds.set(boundsBeforeMaximize.left,
-      STABLE_BOUNDS.top, boundsBeforeMaximize.right, STABLE_BOUNDS.bottom)
-
-    // Restore
-    controller.toggleDesktopTaskSize(
-      task,
-      ToggleTaskSizeInteraction(
-        ToggleTaskSizeInteraction.Direction.RESTORE,
-        ToggleTaskSizeInteraction.Source.HEADER_BUTTON_TO_RESTORE,
-        InputMethod.TOUCH
-      )
-    )
-
-    // Assert bounds set to last bounds before maximize
-    val wct = getLatestToggleResizeDesktopTaskWct()
-    assertThat(findBoundsChange(wct, task)).isEqualTo(boundsBeforeMaximize)
-    verify(desktopModeEventLogger, times(1)).logTaskResizingEnded(
-      ResizeTrigger.MAXIMIZE_BUTTON,
-      InputMethod.TOUCH,
-      task,
-      boundsBeforeMaximize.width(),
-      boundsBeforeMaximize.height(),
-      displayController
-    )
-  }
-
-  @Test
-  fun toggleBounds_removesLastBoundsBeforeMaximizeAfterRestoringBounds() {
-    val boundsBeforeMaximize = Rect(0, 0, 100, 100)
-    val task = setUpFreeformTask(DEFAULT_DISPLAY, boundsBeforeMaximize)
-
-    // Maximize
-    controller.toggleDesktopTaskSize(
-      task,
-      ToggleTaskSizeInteraction(
-        ToggleTaskSizeInteraction.Direction.MAXIMIZE,
-        ToggleTaskSizeInteraction.Source.HEADER_BUTTON_TO_MAXIMIZE,
-        InputMethod.TOUCH
-      )
-    )
-    task.configuration.windowConfiguration.bounds.set(STABLE_BOUNDS)
-
-    // Restore
-    controller.toggleDesktopTaskSize(
-      task,
-      ToggleTaskSizeInteraction(
-        ToggleTaskSizeInteraction.Direction.RESTORE,
-        ToggleTaskSizeInteraction.Source.HEADER_BUTTON_TO_RESTORE,
-        InputMethod.TOUCH
-      )
-    )
-
-    // Assert last bounds before maximize removed after use
-    assertThat(taskRepository.removeBoundsBeforeMaximize(task.taskId)).isNull()
-    verify(desktopModeEventLogger, times(1)).logTaskResizingEnded(
-      ResizeTrigger.MAXIMIZE_BUTTON,
-      InputMethod.TOUCH,
-      task,
-      boundsBeforeMaximize.width(),
-      boundsBeforeMaximize.height(),
-      displayController
-    )
-  }
-
-  @Test
-  fun onUnhandledDrag_newFreeformIntent() {
-    testOnUnhandledDrag(DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR,
-      PointF(1200f, 700f),
-      Rect(240, 700, 2160, 1900))
-  }
-
-  @Test
-  fun onUnhandledDrag_newFreeformIntentSplitLeft() {
-    testOnUnhandledDrag(DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_LEFT_INDICATOR,
-      PointF(50f, 700f),
-      Rect(0, 0, 500, 1000))
-  }
-
-  @Test
-  fun onUnhandledDrag_newFreeformIntentSplitRight() {
-    testOnUnhandledDrag(DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_RIGHT_INDICATOR,
-      PointF(2500f, 700f),
-      Rect(500, 0, 1000, 1000))
-  }
-
-  @Test
-  fun onUnhandledDrag_newFullscreenIntent() {
-    testOnUnhandledDrag(DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR,
-      PointF(1200f, 50f),
-      Rect())
-  }
-
-  @Test
-  fun shellController_registersUserChangeListener() {
-      verify(shellController, times(1)).addUserChangeListener(any())
-  }
-
-  @Test
-  @EnableFlags(FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
-  fun onTaskInfoChanged_inImmersiveUnrequestsImmersive_exits() {
-    val task = setUpFreeformTask(DEFAULT_DISPLAY)
-    taskRepository.setTaskInFullImmersiveState(DEFAULT_DISPLAY, task.taskId, immersive = true)
-
-    task.requestedVisibleTypes = WindowInsets.Type.statusBars()
-    controller.onTaskInfoChanged(task)
-
-    verify(mMockDesktopImmersiveController).moveTaskToNonImmersive(eq(task), any())
-  }
-
-  @Test
-  @EnableFlags(FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
-  fun onTaskInfoChanged_notInImmersiveUnrequestsImmersive_noReExit() {
-    val task = setUpFreeformTask(DEFAULT_DISPLAY)
-    taskRepository.setTaskInFullImmersiveState(DEFAULT_DISPLAY, task.taskId, immersive = false)
-
-    task.requestedVisibleTypes = WindowInsets.Type.statusBars()
-    controller.onTaskInfoChanged(task)
-
-    verify(mMockDesktopImmersiveController, never()).moveTaskToNonImmersive(eq(task), any())
-  }
-
-  @Test
-  @EnableFlags(FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
-  fun onTaskInfoChanged_inImmersiveUnrequestsImmersive_inRecentsTransition_noExit() {
-    val task = setUpFreeformTask(DEFAULT_DISPLAY)
-    taskRepository.setTaskInFullImmersiveState(DEFAULT_DISPLAY, task.taskId, immersive = true)
-    recentsTransitionStateListener.onTransitionStateChanged(TRANSITION_STATE_REQUESTED)
-
-    task.requestedVisibleTypes = WindowInsets.Type.statusBars()
-    controller.onTaskInfoChanged(task)
-
-    verify(mMockDesktopImmersiveController, never()).moveTaskToNonImmersive(eq(task), any())
-  }
-
-  @Test
-  fun moveTaskToDesktop_background_attemptsImmersiveExit() {
-    val task = setUpFreeformTask(background = true)
-    val wct = WindowContainerTransaction()
-    val runOnStartTransit = RunOnStartTransitionCallback()
-    val transition = Binder()
-    whenever(mMockDesktopImmersiveController
-      .exitImmersiveIfApplicable(eq(wct), eq(task.displayId), eq(task.taskId), any()))
-      .thenReturn(
-        ExitResult.Exit(
-        exitingTask = 5,
-        runOnTransitionStart = runOnStartTransit,
-      ))
-    whenever(enterDesktopTransitionHandler.moveToDesktop(wct, UNKNOWN)).thenReturn(transition)
-
-    controller.moveTaskToDesktop(taskId = task.taskId, wct = wct, transitionSource = UNKNOWN)
-
-    verify(mMockDesktopImmersiveController)
-      .exitImmersiveIfApplicable(eq(wct), eq(task.displayId), eq(task.taskId), any())
-    runOnStartTransit.assertOnlyInvocation(transition)
-  }
-
-  @Test
-  fun moveTaskToDesktop_foreground_attemptsImmersiveExit() {
-    val task = setUpFreeformTask(background = false)
-    val wct = WindowContainerTransaction()
-    val runOnStartTransit = RunOnStartTransitionCallback()
-    val transition = Binder()
-    whenever(mMockDesktopImmersiveController
-      .exitImmersiveIfApplicable(eq(wct), eq(task.displayId), eq(task.taskId), any()))
-      .thenReturn(
-        ExitResult.Exit(
-        exitingTask = 5,
-        runOnTransitionStart = runOnStartTransit,
-      ))
-    whenever(enterDesktopTransitionHandler.moveToDesktop(wct, UNKNOWN)).thenReturn(transition)
-
-    controller.moveTaskToDesktop(taskId = task.taskId, wct = wct, transitionSource = UNKNOWN)
-
-    verify(mMockDesktopImmersiveController)
-      .exitImmersiveIfApplicable(eq(wct), eq(task.displayId), eq(task.taskId), any())
-    runOnStartTransit.assertOnlyInvocation(transition)
-  }
-
-  @Test
-  fun moveTaskToFront_background_attemptsImmersiveExit() {
-    val task = setUpFreeformTask(background = true)
-    val runOnStartTransit = RunOnStartTransitionCallback()
-    val transition = Binder()
-    whenever(mMockDesktopImmersiveController
-      .exitImmersiveIfApplicable(any(), eq(task.displayId), eq(task.taskId), any()))
-      .thenReturn(
-        ExitResult.Exit(
-        exitingTask = 5,
-        runOnTransitionStart = runOnStartTransit,
-      ))
-    whenever(desktopMixedTransitionHandler
-      .startLaunchTransition(any(), any(), anyInt(), anyOrNull(), anyOrNull()))
-      .thenReturn(transition)
-
-    controller.moveTaskToFront(task.taskId, remoteTransition = null)
-
-    verify(mMockDesktopImmersiveController)
-      .exitImmersiveIfApplicable(any(), eq(task.displayId), eq(task.taskId), any())
-    runOnStartTransit.assertOnlyInvocation(transition)
-  }
-
-  @Test
-  fun moveTaskToFront_foreground_attemptsImmersiveExit() {
-    val task = setUpFreeformTask(background = false)
-    val runOnStartTransit = RunOnStartTransitionCallback()
-    val transition = Binder()
-    whenever(mMockDesktopImmersiveController
-      .exitImmersiveIfApplicable(any(), eq(task.displayId), eq(task.taskId), any()))
-      .thenReturn(
-        ExitResult.Exit(
-        exitingTask = 5,
-        runOnTransitionStart = runOnStartTransit,
-      ))
-    whenever(desktopMixedTransitionHandler
-      .startLaunchTransition(any(), any(), eq(task.taskId), anyOrNull(), anyOrNull()))
-      .thenReturn(transition)
-
-    controller.moveTaskToFront(task.taskId, remoteTransition = null)
-
-    verify(mMockDesktopImmersiveController)
-      .exitImmersiveIfApplicable(any(), eq(task.displayId), eq(task.taskId), any())
-    runOnStartTransit.assertOnlyInvocation(transition)
-  }
-
-  @Test
-  fun handleRequest_freeformLaunchToDesktop_attemptsImmersiveExit() {
-    markTaskVisible(setUpFreeformTask())
-    val task = setUpFreeformTask()
-    markTaskVisible(task)
-    val binder = Binder()
-
-    controller.handleRequest(binder, createTransition(task))
-
-    verify(mMockDesktopImmersiveController)
-      .exitImmersiveIfApplicable(eq(binder), any(), eq(task.displayId), any())
-  }
-
-  @Test
-  fun handleRequest_fullscreenLaunchToDesktop_attemptsImmersiveExit() {
-    setUpFreeformTask()
-    val task = setUpFullscreenTask()
-    val binder = Binder()
-
-    controller.handleRequest(binder, createTransition(task))
-
-    verify(mMockDesktopImmersiveController)
-      .exitImmersiveIfApplicable(eq(binder), any(), eq(task.displayId), any())
-  }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
-  fun shouldPlayDesktopAnimation_notShowingDesktop_doesNotPlay() {
-    val triggerTask = setUpFullscreenTask(displayId = 5)
-    taskRepository.setTaskInFullImmersiveState(
-      displayId = triggerTask.displayId,
-      taskId = triggerTask.taskId,
-      immersive = true
-    )
-
-    assertThat(controller.shouldPlayDesktopAnimation(
-      TransitionRequestInfo(TRANSIT_OPEN, triggerTask, /* remoteTransition= */ null)
-    )).isFalse()
-  }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
-  fun shouldPlayDesktopAnimation_notOpening_doesNotPlay() {
-    val triggerTask = setUpFreeformTask(displayId = 5)
-    taskRepository.setTaskInFullImmersiveState(
-      displayId = triggerTask.displayId,
-      taskId = triggerTask.taskId,
-      immersive = true
-    )
-
-    assertThat(controller.shouldPlayDesktopAnimation(
-      TransitionRequestInfo(TRANSIT_CHANGE, triggerTask, /* remoteTransition= */ null)
-    )).isFalse()
-  }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
-  fun shouldPlayDesktopAnimation_notImmersive_doesNotPlay() {
-    val triggerTask = setUpFreeformTask(displayId = 5)
-    taskRepository.setTaskInFullImmersiveState(
-      displayId = triggerTask.displayId,
-      taskId = triggerTask.taskId,
-      immersive = false
-    )
-
-    assertThat(controller.shouldPlayDesktopAnimation(
-      TransitionRequestInfo(TRANSIT_OPEN, triggerTask, /* remoteTransition= */ null)
-    )).isFalse()
-  }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
-  fun shouldPlayDesktopAnimation_fullscreenEntersDesktop_plays() {
-    // At least one freeform task to be in a desktop.
-    val existingTask = setUpFreeformTask(displayId = 5)
-    val triggerTask = setUpFullscreenTask(displayId = 5)
-    assertThat(controller.isDesktopModeShowing(triggerTask.displayId)).isTrue()
-    taskRepository.setTaskInFullImmersiveState(
-      displayId = existingTask.displayId,
-      taskId = existingTask.taskId,
-      immersive = true
-    )
-
-    assertThat(
-      controller.shouldPlayDesktopAnimation(
-        TransitionRequestInfo(TRANSIT_OPEN, triggerTask, /* remoteTransition= */ null)
-      )
-    ).isTrue()
-  }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
-  fun shouldPlayDesktopAnimation_fullscreenStaysFullscreen_doesNotPlay() {
-    val triggerTask = setUpFullscreenTask(displayId = 5)
-    assertThat(controller.isDesktopModeShowing(triggerTask.displayId)).isFalse()
-
-    assertThat(controller.shouldPlayDesktopAnimation(
-      TransitionRequestInfo(TRANSIT_OPEN, triggerTask, /* remoteTransition= */ null)
-    )).isFalse()
-  }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
-  fun shouldPlayDesktopAnimation_freeformStaysInDesktop_plays() {
-    // At least one freeform task to be in a desktop.
-    val existingTask = setUpFreeformTask(displayId = 5)
-    val triggerTask = setUpFreeformTask(displayId = 5, active = false)
-    assertThat(controller.isDesktopModeShowing(triggerTask.displayId)).isTrue()
-    taskRepository.setTaskInFullImmersiveState(
-      displayId = existingTask.displayId,
-      taskId = existingTask.taskId,
-      immersive = true
-    )
-
-    assertThat(
-      controller.shouldPlayDesktopAnimation(
-        TransitionRequestInfo(TRANSIT_OPEN, triggerTask, /* remoteTransition= */ null)
-      )
-    ).isTrue()
-  }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
-  fun shouldPlayDesktopAnimation_freeformExitsDesktop_doesNotPlay() {
-    val triggerTask = setUpFreeformTask(displayId = 5, active = false)
-    assertThat(controller.isDesktopModeShowing(triggerTask.displayId)).isFalse()
-
-    assertThat(controller.shouldPlayDesktopAnimation(
-      TransitionRequestInfo(TRANSIT_OPEN, triggerTask, /* remoteTransition= */ null)
-    )).isFalse()
-  }
-
-  private class RunOnStartTransitionCallback : ((IBinder) -> Unit) {
-    var invocations = 0
-      private set
-    var lastInvoked: IBinder? = null
-      private set
-
-    override fun invoke(transition: IBinder) {
-      invocations++
-      lastInvoked = transition
-    }
-  }
-
-  private fun RunOnStartTransitionCallback.assertOnlyInvocation(transition: IBinder) {
-    assertThat(invocations).isEqualTo(1)
-    assertThat(lastInvoked).isEqualTo(transition)
-  }
-
-  /**
-   * Assert that an unhandled drag event launches a PendingIntent with the
-   * windowing mode and bounds we are expecting.
-   */
-  private fun testOnUnhandledDrag(
-    indicatorType: DesktopModeVisualIndicator.IndicatorType,
-    inputCoordinate: PointF,
-    expectedBounds: Rect
-  ) {
-    setUpLandscapeDisplay()
-    val task = setUpFreeformTask()
-    markTaskVisible(task)
-    task.isFocused = true
-    val runningTasks = ArrayList<RunningTaskInfo>()
-    runningTasks.add(task)
-    val spyController = spy(controller)
-    val mockPendingIntent = mock(PendingIntent::class.java)
-    val mockDragEvent = mock(DragEvent::class.java)
-    val mockCallback = mock(Consumer::class.java)
-    val b = SurfaceControl.Builder()
-    b.setName("test surface")
-    val dragSurface = b.build()
-    whenever(shellTaskOrganizer.runningTasks).thenReturn(runningTasks)
-    whenever(mockDragEvent.dragSurface).thenReturn(dragSurface)
-    whenever(mockDragEvent.x).thenReturn(inputCoordinate.x)
-    whenever(mockDragEvent.y).thenReturn(inputCoordinate.y)
-    whenever(multiInstanceHelper.supportsMultiInstanceSplit(anyOrNull())).thenReturn(true)
-    whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
-    doReturn(indicatorType)
-      .whenever(spyController).updateVisualIndicator(
-        eq(task),
-        anyOrNull(),
-        anyOrNull(),
-        anyOrNull(),
-        eq(DesktopModeVisualIndicator.DragStartState.DRAGGED_INTENT)
-      )
-
-    spyController.onUnhandledDrag(
-      mockPendingIntent,
-      mockDragEvent,
-      mockCallback as Consumer<Boolean>
-    )
-    val arg: ArgumentCaptor<WindowContainerTransaction> =
-      ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
-    var expectedWindowingMode: Int
-      if (indicatorType == DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR) {
-        expectedWindowingMode = WINDOWING_MODE_FULLSCREEN
-        // Fullscreen launches currently use default transitions
-        verify(transitions).startTransition(any(), capture(arg), anyOrNull())
-      } else {
-        expectedWindowingMode = WINDOWING_MODE_FREEFORM
-        // All other launches use a special handler.
-        verify(dragAndDropTransitionHandler).handleDropEvent(capture(arg))
-      }
-    assertThat(ActivityOptions.fromBundle(arg.value.hierarchyOps[0].launchOptions)
-      .launchWindowingMode).isEqualTo(expectedWindowingMode)
-    assertThat(ActivityOptions.fromBundle(arg.value.hierarchyOps[0].launchOptions)
-      .launchBounds).isEqualTo(expectedBounds)
-  }
-
-  private val desktopWallpaperIntent: Intent
-    get() = Intent(context, DesktopWallpaperActivity::class.java)
-
-  private fun addFreeformTaskAtPosition(
-    pos: DesktopTaskPosition,
-    stableBounds: Rect,
-    bounds: Rect = DEFAULT_LANDSCAPE_BOUNDS,
-    offsetPos: Point = Point(0, 0)
-  ): RunningTaskInfo {
-    val offset = pos.getTopLeftCoordinates(stableBounds, bounds)
-    val prevTaskBounds = Rect(bounds)
-    prevTaskBounds.offsetTo(offset.x + offsetPos.x, offset.y + offsetPos.y)
-    return setUpFreeformTask(bounds = prevTaskBounds)
-  }
-
-  private fun setUpFreeformTask(
-      displayId: Int = DEFAULT_DISPLAY,
-      bounds: Rect? = null,
-      active: Boolean = true,
-      background: Boolean = false,
-  ): RunningTaskInfo {
-    val task = createFreeformTask(displayId, bounds)
-    val activityInfo = ActivityInfo()
-    task.topActivityInfo = activityInfo
-    if (background) {
-      whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(null)
-      whenever(recentTasksController.findTaskInBackground(task.taskId))
-        .thenReturn(createTaskInfo(task.taskId))
-    } else {
-      whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
-    }
-    taskRepository.addTask(displayId, task.taskId, isVisible = active)
-    if (!background) {
-      runningTasks.add(task)
-    }
-    return task
-  }
-
-  private fun setUpPipTask(autoEnterEnabled: Boolean): RunningTaskInfo {
-    return setUpFreeformTask().apply {
-      pictureInPictureParams = PictureInPictureParams.Builder()
-        .setAutoEnterEnabled(autoEnterEnabled)
-        .build()
-    }
-  }
-
-  private fun setUpHomeTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo {
-    val task = createHomeTask(displayId)
-    whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
-    runningTasks.add(task)
-    return task
-  }
-
-  private fun setUpFullscreenTask(
-    displayId: Int = DEFAULT_DISPLAY,
-    isResizable: Boolean = true,
-    windowingMode: Int = WINDOWING_MODE_FULLSCREEN,
-    deviceOrientation: Int = ORIENTATION_LANDSCAPE,
-    screenOrientation: Int = SCREEN_ORIENTATION_UNSPECIFIED,
-    shouldLetterbox: Boolean = false,
-    gravity: Int = Gravity.NO_GRAVITY,
-    enableUserFullscreenOverride: Boolean = false,
-    enableSystemFullscreenOverride: Boolean = false,
-    aspectRatioOverrideApplied: Boolean = false
-  ): RunningTaskInfo {
-    val task = createFullscreenTask(displayId)
-    val activityInfo = ActivityInfo()
-    activityInfo.screenOrientation = screenOrientation
-    activityInfo.windowLayout = ActivityInfo.WindowLayout(0, 0F, 0, 0F, gravity, 0, 0)
-    with(task) {
-      topActivityInfo = activityInfo
-      isResizeable = isResizable
-      configuration.orientation = deviceOrientation
-      configuration.windowConfiguration.windowingMode = windowingMode
-      appCompatTaskInfo.isUserFullscreenOverrideEnabled = enableUserFullscreenOverride
-      appCompatTaskInfo.isSystemFullscreenOverrideEnabled = enableSystemFullscreenOverride
-
-      if (deviceOrientation == ORIENTATION_LANDSCAPE) {
-        configuration.windowConfiguration.appBounds =
-          Rect(0, 0, DISPLAY_DIMENSION_LONG, DISPLAY_DIMENSION_SHORT)
-        appCompatTaskInfo.topActivityLetterboxAppWidth = DISPLAY_DIMENSION_LONG
-        appCompatTaskInfo.topActivityLetterboxAppHeight = DISPLAY_DIMENSION_SHORT
-      } else {
-        configuration.windowConfiguration.appBounds =
-          Rect(0, 0, DISPLAY_DIMENSION_SHORT, DISPLAY_DIMENSION_LONG)
-        appCompatTaskInfo.topActivityLetterboxAppWidth = DISPLAY_DIMENSION_SHORT
-        appCompatTaskInfo.topActivityLetterboxAppHeight = DISPLAY_DIMENSION_LONG
-      }
-
-      if (shouldLetterbox) {
-        appCompatTaskInfo.setHasMinAspectRatioOverride(aspectRatioOverrideApplied)
-        if (deviceOrientation == ORIENTATION_LANDSCAPE &&
-            screenOrientation == SCREEN_ORIENTATION_PORTRAIT) {
-          // Letterbox to portrait size
-          appCompatTaskInfo.setTopActivityLetterboxed(true)
-          appCompatTaskInfo.topActivityLetterboxAppWidth = 1200
-          appCompatTaskInfo.topActivityLetterboxAppHeight = 1600
-        } else if (deviceOrientation == ORIENTATION_PORTRAIT &&
-            screenOrientation == SCREEN_ORIENTATION_LANDSCAPE) {
-          // Letterbox to landscape size
-          appCompatTaskInfo.setTopActivityLetterboxed(true)
-          appCompatTaskInfo.topActivityLetterboxAppWidth = 1600
-          appCompatTaskInfo.topActivityLetterboxAppHeight = 1200
+        whenever(shellTaskOrganizer.getRunningTasks(anyInt())).thenAnswer { runningTasks }
+        whenever(transitions.startTransition(anyInt(), any(), isNull())).thenAnswer { Binder() }
+        whenever(enterDesktopTransitionHandler.moveToDesktop(any(), any())).thenAnswer { Binder() }
+        whenever(displayController.getDisplayLayout(anyInt())).thenReturn(displayLayout)
+        whenever(displayLayout.getStableBounds(any())).thenAnswer { i ->
+            (i.arguments.first() as Rect).set(STABLE_BOUNDS)
         }
-      }
-    }
-    whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
-    runningTasks.add(task)
-    return task
-  }
+        whenever(displayLayout.densityDpi()).thenReturn(160)
+        whenever(runBlocking { persistentRepository.readDesktop(any(), any()) })
+            .thenReturn(Desktop.getDefaultInstance())
+        doReturn(mockToast).`when` { Toast.makeText(any(), anyInt(), anyInt()) }
 
-  private fun setUpLandscapeDisplay() {
-    whenever(displayLayout.width()).thenReturn(DISPLAY_DIMENSION_LONG)
-    whenever(displayLayout.height()).thenReturn(DISPLAY_DIMENSION_SHORT)
-    val stableBounds = Rect(0, 0, DISPLAY_DIMENSION_LONG,
-      DISPLAY_DIMENSION_SHORT - Companion.TASKBAR_FRAME_HEIGHT
+        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>(),
+                    any(),
+                )
+            )
+            .thenReturn(ExitResult.NoExit)
+        whenever(
+                mMockDesktopImmersiveController.exitImmersiveIfApplicable(
+                    any(),
+                    anyInt(),
+                    anyOrNull(),
+                    any(),
+                )
+            )
+            .thenReturn(ExitResult.NoExit)
+
+        controller = createController()
+        controller.setSplitScreenController(splitScreenController)
+        controller.freeformTaskTransitionStarter = freeformTaskTransitionStarter
+        controller.desktopModeEnterExitTransitionListener = desktopModeEnterExitTransitionListener
+
+        shellInit.init()
+
+        val captor = ArgumentCaptor.forClass(RecentsTransitionStateListener::class.java)
+        verify(recentsTransitionHandler).addTransitionStateListener(captor.capture())
+        recentsTransitionStateListener = captor.value
+
+        controller.taskbarDesktopTaskListener = taskbarDesktopTaskListener
+
+        assumeTrue(ENABLE_SHELL_TRANSITIONS)
+
+        taskRepository = userRepositories.current
+    }
+
+    private fun createController(): DesktopTasksController {
+        return DesktopTasksController(
+            context,
+            shellInit,
+            shellCommandHandler,
+            shellController,
+            displayController,
+            shellTaskOrganizer,
+            syncQueue,
+            rootTaskDisplayAreaOrganizer,
+            dragAndDropController,
+            transitions,
+            keyguardManager,
+            mReturnToDragStartAnimator,
+            desktopMixedTransitionHandler,
+            enterDesktopTransitionHandler,
+            exitDesktopTransitionHandler,
+            dragAndDropTransitionHandler,
+            toggleResizeDesktopTaskTransitionHandler,
+            dragToDesktopTransitionHandler,
+            mMockDesktopImmersiveController,
+            userRepositories,
+            recentsTransitionHandler,
+            multiInstanceHelper,
+            shellExecutor,
+            Optional.of(desktopTasksLimiter),
+            recentTasksController,
+            mockInteractionJankMonitor,
+            mockHandler,
+            desktopModeEventLogger,
+            desktopModeUiEventLogger,
+            desktopTilingDecorViewModel,
+        )
+    }
+
+    @After
+    fun tearDown() {
+        mockitoSession.finishMocking()
+
+        runningTasks.clear()
+        testScope.cancel()
+    }
+
+    @Test
+    fun instantiate_addInitCallback() {
+        verify(shellInit).addInitCallback(any(), any<DesktopTasksController>())
+    }
+
+    @Test
+    fun doesAnyTaskRequireTaskbarRounding_onlyFreeFormTaskIsRunning_returnFalse() {
+        setUpFreeformTask()
+
+        assertThat(controller.doesAnyTaskRequireTaskbarRounding(DEFAULT_DISPLAY)).isFalse()
+    }
+
+    @Test
+    fun doesAnyTaskRequireTaskbarRounding_toggleResizeOfFreeFormTask_returnTrue() {
+        val task1 = setUpFreeformTask()
+
+        val argumentCaptor = ArgumentCaptor.forClass(Boolean::class.java)
+        controller.toggleDesktopTaskSize(
+            task1,
+            ToggleTaskSizeInteraction(
+                ToggleTaskSizeInteraction.Direction.MAXIMIZE,
+                ToggleTaskSizeInteraction.Source.HEADER_BUTTON_TO_MAXIMIZE,
+                InputMethod.TOUCH,
+            ),
+        )
+
+        verify(taskbarDesktopTaskListener).onTaskbarCornerRoundingUpdate(argumentCaptor.capture())
+        verify(desktopModeEventLogger, times(1))
+            .logTaskResizingEnded(
+                ResizeTrigger.MAXIMIZE_BUTTON,
+                InputMethod.TOUCH,
+                task1,
+                STABLE_BOUNDS.width(),
+                STABLE_BOUNDS.height(),
+                displayController,
+            )
+        assertThat(argumentCaptor.value).isTrue()
+    }
+
+    @Test
+    fun doesAnyTaskRequireTaskbarRounding_fullScreenTaskIsRunning_returnTrue() {
+        val stableBounds = Rect().apply { displayLayout.getStableBounds(this) }
+        setUpFreeformTask(bounds = stableBounds, active = true)
+        assertThat(controller.doesAnyTaskRequireTaskbarRounding(DEFAULT_DISPLAY)).isTrue()
+    }
+
+    @Test
+    fun doesAnyTaskRequireTaskbarRounding_toggleResizeOfMaximizedTask_returnFalse() {
+        val stableBounds = Rect().apply { displayLayout.getStableBounds(this) }
+        val task1 = setUpFreeformTask(bounds = stableBounds, active = true)
+
+        val argumentCaptor = ArgumentCaptor.forClass(Boolean::class.java)
+        controller.toggleDesktopTaskSize(
+            task1,
+            ToggleTaskSizeInteraction(
+                ToggleTaskSizeInteraction.Direction.RESTORE,
+                ToggleTaskSizeInteraction.Source.HEADER_BUTTON_TO_RESTORE,
+                InputMethod.TOUCH,
+            ),
+        )
+
+        verify(taskbarDesktopTaskListener).onTaskbarCornerRoundingUpdate(argumentCaptor.capture())
+        verify(desktopModeEventLogger, times(1))
+            .logTaskResizingEnded(
+                eq(ResizeTrigger.MAXIMIZE_BUTTON),
+                eq(InputMethod.TOUCH),
+                eq(task1),
+                anyOrNull(),
+                anyOrNull(),
+                eq(displayController),
+                anyOrNull(),
+            )
+        assertThat(argumentCaptor.value).isFalse()
+    }
+
+    @Test
+    fun doesAnyTaskRequireTaskbarRounding_splitScreenTaskIsRunning_returnTrue() {
+        val stableBounds = Rect().apply { displayLayout.getStableBounds(this) }
+        setUpFreeformTask(
+            bounds = Rect(stableBounds.left, stableBounds.top, 500, stableBounds.bottom)
+        )
+
+        assertThat(controller.doesAnyTaskRequireTaskbarRounding(DEFAULT_DISPLAY)).isTrue()
+    }
+
+    @Test
+    fun instantiate_cannotEnterDesktopMode_doNotAddInitCallback() {
+        whenever(DesktopModeStatus.canEnterDesktopMode(context)).thenReturn(false)
+        clearInvocations(shellInit)
+
+        createController()
+
+        verify(shellInit, never()).addInitCallback(any(), any<DesktopTasksController>())
+    }
+
+    @Test
+    @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+    fun showDesktopApps_allAppsInvisible_bringsToFront_desktopWallpaperDisabled() {
+        val homeTask = setUpHomeTask()
+        val task1 = setUpFreeformTask()
+        val task2 = setUpFreeformTask()
+        markTaskHidden(task1)
+        markTaskHidden(task2)
+
+        controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
+
+        val wct =
+            getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
+        assertThat(wct.hierarchyOps).hasSize(3)
+        // Expect order to be from bottom: home, task1, task2
+        wct.assertReorderAt(index = 0, homeTask)
+        wct.assertReorderAt(index = 1, task1)
+        wct.assertReorderAt(index = 2, task2)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+    fun showDesktopApps_allAppsInvisible_bringsToFront_desktopWallpaperEnabled() {
+        val task1 = setUpFreeformTask()
+        val task2 = setUpFreeformTask()
+        markTaskHidden(task1)
+        markTaskHidden(task2)
+
+        controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
+
+        val wct =
+            getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
+        assertThat(wct.hierarchyOps).hasSize(3)
+        // Expect order to be from bottom: wallpaper intent, task1, task2
+        wct.assertPendingIntentAt(index = 0, desktopWallpaperIntent)
+        wct.assertReorderAt(index = 1, task1)
+        wct.assertReorderAt(index = 2, task2)
+    }
+
+    @Test
+    fun isDesktopModeShowing_noTasks_returnsFalse() {
+        assertThat(controller.isDesktopModeShowing(displayId = 0)).isFalse()
+    }
+
+    @Test
+    fun isDesktopModeShowing_noTasksVisible_returnsFalse() {
+        val task1 = setUpFreeformTask()
+        val task2 = setUpFreeformTask()
+        markTaskHidden(task1)
+        markTaskHidden(task2)
+
+        assertThat(controller.isDesktopModeShowing(displayId = 0)).isFalse()
+    }
+
+    @Test
+    fun isDesktopModeShowing_tasksActiveAndVisible_returnsTrue() {
+        val task1 = setUpFreeformTask()
+        val task2 = setUpFreeformTask()
+        markTaskVisible(task1)
+        markTaskHidden(task2)
+
+        assertThat(controller.isDesktopModeShowing(displayId = 0)).isTrue()
+    }
+
+    @Test
+    @EnableFlags(
+        Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY,
+        Flags.FLAG_INCLUDE_TOP_TRANSPARENT_FULLSCREEN_TASK_IN_DESKTOP_HEURISTIC,
     )
-    whenever(displayLayout.getStableBoundsForDesktopMode(any())).thenAnswer { i ->
-      (i.arguments.first() as Rect).set(stableBounds)
-    }
-  }
+    fun isDesktopModeShowing_topTransparentFullscreenTask_returnsTrue() {
+        val topTransparentTask = setUpFullscreenTask(displayId = DEFAULT_DISPLAY)
+        taskRepository.setTopTransparentFullscreenTaskId(DEFAULT_DISPLAY, topTransparentTask.taskId)
 
-  private fun setUpPortraitDisplay() {
-    whenever(displayLayout.width()).thenReturn(DISPLAY_DIMENSION_SHORT)
-    whenever(displayLayout.height()).thenReturn(DISPLAY_DIMENSION_LONG)
-    val stableBounds = Rect(0, 0, DISPLAY_DIMENSION_SHORT,
-      DISPLAY_DIMENSION_LONG - Companion.TASKBAR_FRAME_HEIGHT
+        assertThat(controller.isDesktopModeShowing(displayId = DEFAULT_DISPLAY)).isTrue()
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+    fun showDesktopApps_onSecondaryDisplay_desktopWallpaperEnabled_shouldNotShowWallpaper() {
+        val homeTask = setUpHomeTask(SECOND_DISPLAY)
+        val task1 = setUpFreeformTask(SECOND_DISPLAY)
+        val task2 = setUpFreeformTask(SECOND_DISPLAY)
+        markTaskHidden(task1)
+        markTaskHidden(task2)
+
+        controller.showDesktopApps(SECOND_DISPLAY, RemoteTransition(TestRemoteTransition()))
+
+        val wct =
+            getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
+        assertThat(wct.hierarchyOps).hasSize(3)
+        // Expect order to be from bottom: home, task1, task2 (no wallpaper intent)
+        wct.assertReorderAt(index = 0, homeTask)
+        wct.assertReorderAt(index = 1, task1)
+        wct.assertReorderAt(index = 2, task2)
+    }
+
+    @Test
+    @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+    fun showDesktopApps_appsAlreadyVisible_bringsToFront_desktopWallpaperDisabled() {
+        val homeTask = setUpHomeTask()
+        val task1 = setUpFreeformTask()
+        val task2 = setUpFreeformTask()
+        markTaskVisible(task1)
+        markTaskVisible(task2)
+
+        controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
+
+        val wct =
+            getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
+        assertThat(wct.hierarchyOps).hasSize(3)
+        // Expect order to be from bottom: home, task1, task2
+        wct.assertReorderAt(index = 0, homeTask)
+        wct.assertReorderAt(index = 1, task1)
+        wct.assertReorderAt(index = 2, task2)
+    }
+
+    @Test
+    @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+    fun showDesktopApps_onSecondaryDisplay_desktopWallpaperDisabled_shouldNotMoveLauncher() {
+        val homeTask = setUpHomeTask(SECOND_DISPLAY)
+        val task1 = setUpFreeformTask(SECOND_DISPLAY)
+        val task2 = setUpFreeformTask(SECOND_DISPLAY)
+        markTaskHidden(task1)
+        markTaskHidden(task2)
+
+        controller.showDesktopApps(SECOND_DISPLAY, RemoteTransition(TestRemoteTransition()))
+
+        val wct =
+            getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
+        assertThat(wct.hierarchyOps).hasSize(3)
+        // Expect order to be from bottom: home, task1, task2
+        wct.assertReorderAt(index = 0, homeTask)
+        wct.assertReorderAt(index = 1, task1)
+        wct.assertReorderAt(index = 2, task2)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+    fun showDesktopApps_appsAlreadyVisible_bringsToFront_desktopWallpaperEnabled() {
+        val task1 = setUpFreeformTask()
+        val task2 = setUpFreeformTask()
+        markTaskVisible(task1)
+        markTaskVisible(task2)
+
+        controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
+
+        val wct =
+            getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
+        assertThat(wct.hierarchyOps).hasSize(3)
+        // Expect order to be from bottom: wallpaper intent, task1, task2
+        wct.assertPendingIntentAt(index = 0, desktopWallpaperIntent)
+        wct.assertReorderAt(index = 1, task1)
+        wct.assertReorderAt(index = 2, task2)
+    }
+
+    @Test
+    @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+    fun showDesktopApps_someAppsInvisible_reordersAll_desktopWallpaperDisabled() {
+        val homeTask = setUpHomeTask()
+        val task1 = setUpFreeformTask()
+        val task2 = setUpFreeformTask()
+        markTaskHidden(task1)
+        markTaskVisible(task2)
+
+        controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
+
+        val wct =
+            getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
+        assertThat(wct.hierarchyOps).hasSize(3)
+        // Expect order to be from bottom: home, task1, task2
+        wct.assertReorderAt(index = 0, homeTask)
+        wct.assertReorderAt(index = 1, task1)
+        wct.assertReorderAt(index = 2, task2)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+    fun showDesktopApps_someAppsInvisible_reordersAll_desktopWallpaperEnabled() {
+        val task1 = setUpFreeformTask()
+        val task2 = setUpFreeformTask()
+        markTaskHidden(task1)
+        markTaskVisible(task2)
+
+        controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
+
+        val wct =
+            getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
+        assertThat(wct.hierarchyOps).hasSize(3)
+        // Expect order to be from bottom: wallpaper intent, task1, task2
+        wct.assertPendingIntentAt(index = 0, desktopWallpaperIntent)
+        wct.assertReorderAt(index = 1, task1)
+        wct.assertReorderAt(index = 2, task2)
+    }
+
+    @Test
+    @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+    fun showDesktopApps_noActiveTasks_reorderHomeToTop_desktopWallpaperDisabled() {
+        val homeTask = setUpHomeTask()
+
+        controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
+
+        val wct =
+            getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
+        assertThat(wct.hierarchyOps).hasSize(1)
+        wct.assertReorderAt(index = 0, homeTask)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+    fun showDesktopApps_noActiveTasks_addDesktopWallpaper_desktopWallpaperEnabled() {
+        controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
+
+        val wct =
+            getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
+        wct.assertPendingIntentAt(index = 0, desktopWallpaperIntent)
+    }
+
+    @Test
+    @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+    fun showDesktopApps_twoDisplays_bringsToFrontOnlyOneDisplay_desktopWallpaperDisabled() {
+        val homeTaskDefaultDisplay = setUpHomeTask(DEFAULT_DISPLAY)
+        val taskDefaultDisplay = setUpFreeformTask(DEFAULT_DISPLAY)
+        setUpHomeTask(SECOND_DISPLAY)
+        val taskSecondDisplay = setUpFreeformTask(SECOND_DISPLAY)
+        markTaskHidden(taskDefaultDisplay)
+        markTaskHidden(taskSecondDisplay)
+
+        controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
+
+        val wct =
+            getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
+        assertThat(wct.hierarchyOps).hasSize(2)
+        // Expect order to be from bottom: home, task
+        wct.assertReorderAt(index = 0, homeTaskDefaultDisplay)
+        wct.assertReorderAt(index = 1, taskDefaultDisplay)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+    fun showDesktopApps_twoDisplays_bringsToFrontOnlyOneDisplay_desktopWallpaperEnabled() {
+        val homeTaskDefaultDisplay = setUpHomeTask(DEFAULT_DISPLAY)
+        val taskDefaultDisplay = setUpFreeformTask(DEFAULT_DISPLAY)
+        setUpHomeTask(SECOND_DISPLAY)
+        val taskSecondDisplay = setUpFreeformTask(SECOND_DISPLAY)
+        markTaskHidden(taskDefaultDisplay)
+        markTaskHidden(taskSecondDisplay)
+
+        controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
+
+        val wct =
+            getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
+        assertThat(wct.hierarchyOps).hasSize(3)
+        // Move home to front
+        wct.assertReorderAt(index = 0, homeTaskDefaultDisplay)
+        // Add desktop wallpaper activity
+        wct.assertPendingIntentAt(index = 1, desktopWallpaperIntent)
+        // Move freeform task to front
+        wct.assertReorderAt(index = 2, taskDefaultDisplay)
+    }
+
+    @Test
+    @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+    fun showDesktopApps_desktopWallpaperDisabled_dontReorderMinimizedTask() {
+        val homeTask = setUpHomeTask()
+        val freeformTask = setUpFreeformTask()
+        val minimizedTask = setUpFreeformTask()
+
+        markTaskHidden(freeformTask)
+        markTaskHidden(minimizedTask)
+        taskRepository.minimizeTask(DEFAULT_DISPLAY, minimizedTask.taskId)
+        controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
+
+        val wct =
+            getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
+        assertThat(wct.hierarchyOps).hasSize(2)
+        // Reorder home and freeform task to top, don't reorder the minimized task
+        wct.assertReorderAt(index = 0, homeTask, toTop = true)
+        wct.assertReorderAt(index = 1, freeformTask, toTop = true)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+    fun showDesktopApps_desktopWallpaperEnabled_dontReorderMinimizedTask() {
+        val homeTask = setUpHomeTask()
+        val freeformTask = setUpFreeformTask()
+        val minimizedTask = setUpFreeformTask()
+
+        markTaskHidden(freeformTask)
+        markTaskHidden(minimizedTask)
+        taskRepository.minimizeTask(DEFAULT_DISPLAY, minimizedTask.taskId)
+        controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
+
+        val wct =
+            getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
+        assertThat(wct.hierarchyOps).hasSize(3)
+        // Move home to front
+        wct.assertReorderAt(index = 0, homeTask, toTop = true)
+        // Add desktop wallpaper activity
+        wct.assertPendingIntentAt(index = 1, desktopWallpaperIntent)
+        // Reorder freeform task to top, don't reorder the minimized task
+        wct.assertReorderAt(index = 2, freeformTask, toTop = true)
+    }
+
+    @Test
+    fun visibleTaskCount_noTasks_returnsZero() {
+        assertThat(controller.visibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(0)
+    }
+
+    @Test
+    fun visibleTaskCount_twoTasks_bothVisible_returnsTwo() {
+        setUpHomeTask()
+        setUpFreeformTask().also(::markTaskVisible)
+        setUpFreeformTask().also(::markTaskVisible)
+        assertThat(controller.visibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(2)
+    }
+
+    @Test
+    fun visibleTaskCount_twoTasks_oneVisible_returnsOne() {
+        setUpHomeTask()
+        setUpFreeformTask().also(::markTaskVisible)
+        setUpFreeformTask().also(::markTaskHidden)
+        assertThat(controller.visibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(1)
+    }
+
+    @Test
+    fun visibleTaskCount_twoTasksVisibleOnDifferentDisplays_returnsOne() {
+        setUpHomeTask()
+        setUpFreeformTask(DEFAULT_DISPLAY).also(::markTaskVisible)
+        setUpFreeformTask(SECOND_DISPLAY).also(::markTaskVisible)
+        assertThat(controller.visibleTaskCount(SECOND_DISPLAY)).isEqualTo(1)
+    }
+
+    @Test
+    fun addMoveToDesktopChanges_gravityLeft_noBoundsApplied() {
+        setUpLandscapeDisplay()
+        val task = setUpFullscreenTask(gravity = Gravity.LEFT)
+        val wct = WindowContainerTransaction()
+        controller.addMoveToDesktopChanges(wct, task)
+
+        val finalBounds = findBoundsChange(wct, task)
+        assertThat(finalBounds).isEqualTo(Rect())
+    }
+
+    @Test
+    fun addMoveToDesktopChanges_gravityRight_noBoundsApplied() {
+        setUpLandscapeDisplay()
+        val task = setUpFullscreenTask(gravity = Gravity.RIGHT)
+        val wct = WindowContainerTransaction()
+        controller.addMoveToDesktopChanges(wct, task)
+
+        val finalBounds = findBoundsChange(wct, task)
+        assertThat(finalBounds).isEqualTo(Rect())
+    }
+
+    @Test
+    fun addMoveToDesktopChanges_gravityTop_noBoundsApplied() {
+        setUpLandscapeDisplay()
+        val task = setUpFullscreenTask(gravity = Gravity.TOP)
+        val wct = WindowContainerTransaction()
+        controller.addMoveToDesktopChanges(wct, task)
+
+        val finalBounds = findBoundsChange(wct, task)
+        assertThat(finalBounds).isEqualTo(Rect())
+    }
+
+    @Test
+    fun addMoveToDesktopChanges_gravityBottom_noBoundsApplied() {
+        setUpLandscapeDisplay()
+        val task = setUpFullscreenTask(gravity = Gravity.BOTTOM)
+        val wct = WindowContainerTransaction()
+        controller.addMoveToDesktopChanges(wct, task)
+
+        val finalBounds = findBoundsChange(wct, task)
+        assertThat(finalBounds).isEqualTo(Rect())
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_CASCADING_WINDOWS)
+    fun handleRequest_newFreeformTaskLaunch_cascadeApplied() {
+        setUpLandscapeDisplay()
+        val stableBounds = Rect()
+        displayLayout.getStableBoundsForDesktopMode(stableBounds)
+
+        setUpFreeformTask(bounds = DEFAULT_LANDSCAPE_BOUNDS)
+        val freeformTask = setUpFreeformTask(bounds = DEFAULT_LANDSCAPE_BOUNDS, active = false)
+
+        val wct = controller.handleRequest(Binder(), createTransition(freeformTask))
+
+        assertNotNull(wct, "should handle request")
+        val finalBounds = findBoundsChange(wct, freeformTask)
+        assertThat(stableBounds.getDesktopTaskPosition(finalBounds!!))
+            .isEqualTo(DesktopTaskPosition.BottomRight)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_CASCADING_WINDOWS)
+    fun handleRequest_freeformTaskAlreadyExistsInDesktopMode_cascadeNotApplied() {
+        setUpLandscapeDisplay()
+        val stableBounds = Rect()
+        displayLayout.getStableBoundsForDesktopMode(stableBounds)
+
+        setUpFreeformTask(bounds = DEFAULT_LANDSCAPE_BOUNDS)
+        val freeformTask = setUpFreeformTask(bounds = DEFAULT_LANDSCAPE_BOUNDS)
+
+        val wct = controller.handleRequest(Binder(), createTransition(freeformTask))
+
+        assertNull(wct, "should not handle request")
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_CASCADING_WINDOWS)
+    fun addMoveToDesktopChanges_positionBottomRight() {
+        setUpLandscapeDisplay()
+        val stableBounds = Rect()
+        displayLayout.getStableBoundsForDesktopMode(stableBounds)
+
+        setUpFreeformTask(bounds = DEFAULT_LANDSCAPE_BOUNDS)
+
+        val task = setUpFullscreenTask()
+        val wct = WindowContainerTransaction()
+        controller.addMoveToDesktopChanges(wct, task)
+
+        val finalBounds = findBoundsChange(wct, task)
+        assertThat(stableBounds.getDesktopTaskPosition(finalBounds!!))
+            .isEqualTo(DesktopTaskPosition.BottomRight)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_CASCADING_WINDOWS)
+    fun addMoveToDesktopChanges_positionTopLeft() {
+        setUpLandscapeDisplay()
+        val stableBounds = Rect()
+        displayLayout.getStableBoundsForDesktopMode(stableBounds)
+
+        addFreeformTaskAtPosition(DesktopTaskPosition.BottomRight, stableBounds)
+
+        val task = setUpFullscreenTask()
+        val wct = WindowContainerTransaction()
+        controller.addMoveToDesktopChanges(wct, task)
+
+        val finalBounds = findBoundsChange(wct, task)
+        assertThat(stableBounds.getDesktopTaskPosition(finalBounds!!))
+            .isEqualTo(DesktopTaskPosition.TopLeft)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_CASCADING_WINDOWS)
+    fun addMoveToDesktopChanges_positionBottomLeft() {
+        setUpLandscapeDisplay()
+        val stableBounds = Rect()
+        displayLayout.getStableBoundsForDesktopMode(stableBounds)
+
+        addFreeformTaskAtPosition(DesktopTaskPosition.TopLeft, stableBounds)
+
+        val task = setUpFullscreenTask()
+        val wct = WindowContainerTransaction()
+        controller.addMoveToDesktopChanges(wct, task)
+
+        val finalBounds = findBoundsChange(wct, task)
+        assertThat(stableBounds.getDesktopTaskPosition(finalBounds!!))
+            .isEqualTo(DesktopTaskPosition.BottomLeft)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_CASCADING_WINDOWS)
+    fun addMoveToDesktopChanges_positionTopRight() {
+        setUpLandscapeDisplay()
+        val stableBounds = Rect()
+        displayLayout.getStableBoundsForDesktopMode(stableBounds)
+
+        addFreeformTaskAtPosition(DesktopTaskPosition.BottomLeft, stableBounds)
+
+        val task = setUpFullscreenTask()
+        val wct = WindowContainerTransaction()
+        controller.addMoveToDesktopChanges(wct, task)
+
+        val finalBounds = findBoundsChange(wct, task)
+        assertThat(stableBounds.getDesktopTaskPosition(finalBounds!!))
+            .isEqualTo(DesktopTaskPosition.TopRight)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_CASCADING_WINDOWS)
+    fun addMoveToDesktopChanges_positionResetsToCenter() {
+        setUpLandscapeDisplay()
+        val stableBounds = Rect()
+        displayLayout.getStableBoundsForDesktopMode(stableBounds)
+
+        addFreeformTaskAtPosition(DesktopTaskPosition.TopRight, stableBounds)
+
+        val task = setUpFullscreenTask()
+        val wct = WindowContainerTransaction()
+        controller.addMoveToDesktopChanges(wct, task)
+
+        val finalBounds = findBoundsChange(wct, task)
+        assertThat(stableBounds.getDesktopTaskPosition(finalBounds!!))
+            .isEqualTo(DesktopTaskPosition.Center)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_CASCADING_WINDOWS)
+    fun addMoveToDesktopChanges_lastWindowSnapLeft_positionResetsToCenter() {
+        setUpLandscapeDisplay()
+        val stableBounds = Rect()
+        displayLayout.getStableBoundsForDesktopMode(stableBounds)
+
+        // Add freeform task with half display size snap bounds at left side.
+        setUpFreeformTask(
+            bounds = Rect(stableBounds.left, stableBounds.top, 500, stableBounds.bottom)
+        )
+
+        val task = setUpFullscreenTask()
+        val wct = WindowContainerTransaction()
+        controller.addMoveToDesktopChanges(wct, task)
+
+        val finalBounds = findBoundsChange(wct, task)
+        assertThat(stableBounds.getDesktopTaskPosition(finalBounds!!))
+            .isEqualTo(DesktopTaskPosition.Center)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_CASCADING_WINDOWS)
+    fun addMoveToDesktopChanges_lastWindowSnapRight_positionResetsToCenter() {
+        setUpLandscapeDisplay()
+        val stableBounds = Rect()
+        displayLayout.getStableBoundsForDesktopMode(stableBounds)
+
+        // Add freeform task with half display size snap bounds at right side.
+        setUpFreeformTask(
+            bounds =
+                Rect(
+                    stableBounds.right - 500,
+                    stableBounds.top,
+                    stableBounds.right,
+                    stableBounds.bottom,
+                )
+        )
+
+        val task = setUpFullscreenTask()
+        val wct = WindowContainerTransaction()
+        controller.addMoveToDesktopChanges(wct, task)
+
+        val finalBounds = findBoundsChange(wct, task)
+        assertThat(stableBounds.getDesktopTaskPosition(finalBounds!!))
+            .isEqualTo(DesktopTaskPosition.Center)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_CASCADING_WINDOWS)
+    fun addMoveToDesktopChanges_lastWindowMaximised_positionResetsToCenter() {
+        setUpLandscapeDisplay()
+        val stableBounds = Rect()
+        displayLayout.getStableBoundsForDesktopMode(stableBounds)
+
+        // Add maximised freeform task.
+        setUpFreeformTask(bounds = Rect(stableBounds))
+
+        val task = setUpFullscreenTask()
+        val wct = WindowContainerTransaction()
+        controller.addMoveToDesktopChanges(wct, task)
+
+        val finalBounds = findBoundsChange(wct, task)
+        assertThat(stableBounds.getDesktopTaskPosition(finalBounds!!))
+            .isEqualTo(DesktopTaskPosition.Center)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_CASCADING_WINDOWS)
+    fun addMoveToDesktopChanges_defaultToCenterIfFree() {
+        setUpLandscapeDisplay()
+        val stableBounds = Rect()
+        displayLayout.getStableBoundsForDesktopMode(stableBounds)
+
+        val minTouchTarget =
+            context.resources.getDimensionPixelSize(
+                R.dimen.freeform_required_visible_empty_space_in_header
+            )
+        addFreeformTaskAtPosition(
+            DesktopTaskPosition.Center,
+            stableBounds,
+            Rect(0, 0, 1600, 1200),
+            Point(0, minTouchTarget + 1),
+        )
+
+        val task = setUpFullscreenTask()
+        val wct = WindowContainerTransaction()
+        controller.addMoveToDesktopChanges(wct, task)
+
+        val finalBounds = findBoundsChange(wct, task)
+        assertThat(stableBounds.getDesktopTaskPosition(finalBounds!!))
+            .isEqualTo(DesktopTaskPosition.Center)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+    fun addMoveToDesktopChanges_landscapeDevice_userFullscreenOverride_defaultPortraitBounds() {
+        setUpLandscapeDisplay()
+        val task = setUpFullscreenTask(enableUserFullscreenOverride = true)
+        val wct = WindowContainerTransaction()
+        controller.addMoveToDesktopChanges(wct, task)
+
+        assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_LANDSCAPE_BOUNDS)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+    fun addMoveToDesktopChanges_landscapeDevice_systemFullscreenOverride_defaultPortraitBounds() {
+        setUpLandscapeDisplay()
+        val task = setUpFullscreenTask(enableSystemFullscreenOverride = true)
+        val wct = WindowContainerTransaction()
+        controller.addMoveToDesktopChanges(wct, task)
+
+        assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_LANDSCAPE_BOUNDS)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+    fun addMoveToDesktopChanges_landscapeDevice_portraitResizableApp_aspectRatioOverridden() {
+        setUpLandscapeDisplay()
+        val task =
+            setUpFullscreenTask(
+                screenOrientation = SCREEN_ORIENTATION_PORTRAIT,
+                shouldLetterbox = true,
+                aspectRatioOverrideApplied = true,
+            )
+        val wct = WindowContainerTransaction()
+        controller.addMoveToDesktopChanges(wct, task)
+
+        assertThat(findBoundsChange(wct, task)).isEqualTo(UNRESIZABLE_PORTRAIT_BOUNDS)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+    fun addMoveToDesktopChanges_portraitDevice_userFullscreenOverride_defaultPortraitBounds() {
+        setUpPortraitDisplay()
+        val task = setUpFullscreenTask(enableUserFullscreenOverride = true)
+        val wct = WindowContainerTransaction()
+        controller.addMoveToDesktopChanges(wct, task)
+
+        assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_PORTRAIT_BOUNDS)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+    fun addMoveToDesktopChanges_portraitDevice_systemFullscreenOverride_defaultPortraitBounds() {
+        setUpPortraitDisplay()
+        val task = setUpFullscreenTask(enableSystemFullscreenOverride = true)
+        val wct = WindowContainerTransaction()
+        controller.addMoveToDesktopChanges(wct, task)
+
+        assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_PORTRAIT_BOUNDS)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+    fun addMoveToDesktopChanges_portraitDevice_landscapeResizableApp_aspectRatioOverridden() {
+        setUpPortraitDisplay()
+        val task =
+            setUpFullscreenTask(
+                screenOrientation = SCREEN_ORIENTATION_LANDSCAPE,
+                deviceOrientation = ORIENTATION_PORTRAIT,
+                shouldLetterbox = true,
+                aspectRatioOverrideApplied = true,
+            )
+        val wct = WindowContainerTransaction()
+        controller.addMoveToDesktopChanges(wct, task)
+
+        assertThat(findBoundsChange(wct, task)).isEqualTo(UNRESIZABLE_LANDSCAPE_BOUNDS)
+    }
+
+    @Test
+    fun moveToDesktop_tdaFullscreen_windowingModeSetToFreeform() {
+        val task = setUpFullscreenTask()
+        val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!!
+        tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN
+        controller.moveRunningTaskToDesktop(task, transitionSource = UNKNOWN)
+        val wct = getLatestEnterDesktopWct()
+        assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
+            .isEqualTo(WINDOWING_MODE_FREEFORM)
+        verify(desktopModeEnterExitTransitionListener)
+            .onEnterDesktopModeTransitionStarted(FREEFORM_ANIMATION_DURATION)
+    }
+
+    @Test
+    fun moveRunningTaskToDesktop_tdaFreeform_windowingModeSetToUndefined() {
+        val task = setUpFullscreenTask()
+        val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!!
+        tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM
+        controller.moveRunningTaskToDesktop(task, transitionSource = UNKNOWN)
+        val wct = getLatestEnterDesktopWct()
+        assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
+            .isEqualTo(WINDOWING_MODE_UNDEFINED)
+        verify(desktopModeEnterExitTransitionListener)
+            .onEnterDesktopModeTransitionStarted(FREEFORM_ANIMATION_DURATION)
+    }
+
+    @Test
+    fun moveTaskToDesktop_nonExistentTask_doesNothing() {
+        controller.moveTaskToDesktop(999, transitionSource = UNKNOWN)
+        verifyEnterDesktopWCTNotExecuted()
+        verify(desktopModeEnterExitTransitionListener, times(0))
+            .onEnterDesktopModeTransitionStarted(anyInt())
+    }
+
+    @Test
+    @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+    fun moveTaskToDesktop_desktopWallpaperDisabled_nonRunningTask_launchesInFreeform() {
+        val task = createTaskInfo(1)
+        whenever(shellTaskOrganizer.getRunningTaskInfo(anyInt())).thenReturn(null)
+        whenever(recentTasksController.findTaskInBackground(anyInt())).thenReturn(task)
+
+        controller.moveTaskToDesktop(task.taskId, transitionSource = UNKNOWN)
+
+        with(getLatestEnterDesktopWct()) {
+            assertLaunchTaskAt(0, task.taskId, WINDOWING_MODE_FREEFORM)
+        }
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+    fun moveTaskToDesktop_desktopWallpaperEnabled_nonRunningTask_launchesInFreeform() {
+        val task = createTaskInfo(1)
+        whenever(shellTaskOrganizer.getRunningTaskInfo(anyInt())).thenReturn(null)
+        whenever(recentTasksController.findTaskInBackground(anyInt())).thenReturn(task)
+
+        controller.moveTaskToDesktop(task.taskId, transitionSource = UNKNOWN)
+
+        with(getLatestEnterDesktopWct()) {
+            // Add desktop wallpaper activity
+            assertPendingIntentAt(index = 0, desktopWallpaperIntent)
+            // Launch task
+            assertLaunchTaskAt(index = 1, task.taskId, WINDOWING_MODE_FREEFORM)
+        }
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
+    fun moveRunningTaskToDesktop_topActivityTranslucentWithoutDisplay_taskIsMovedToDesktop() {
+        val task =
+            setUpFullscreenTask().apply {
+                isActivityStackTransparent = true
+                isTopActivityNoDisplay = true
+                numActivities = 1
+            }
+
+        controller.moveRunningTaskToDesktop(task, transitionSource = UNKNOWN)
+        val wct = getLatestEnterDesktopWct()
+        assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
+            .isEqualTo(WINDOWING_MODE_FREEFORM)
+        verify(desktopModeEnterExitTransitionListener)
+            .onEnterDesktopModeTransitionStarted(FREEFORM_ANIMATION_DURATION)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
+    fun moveRunningTaskToDesktop_topActivityTranslucentWithDisplay_doesNothing() {
+        val task =
+            setUpFullscreenTask().apply {
+                isActivityStackTransparent = true
+                isTopActivityNoDisplay = false
+                numActivities = 1
+            }
+
+        controller.moveRunningTaskToDesktop(task, transitionSource = UNKNOWN)
+        verifyEnterDesktopWCTNotExecuted()
+        verify(desktopModeEnterExitTransitionListener, times(0))
+            .onEnterDesktopModeTransitionStarted(FREEFORM_ANIMATION_DURATION)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
+    fun moveRunningTaskToDesktop_systemUIActivityWithDisplay_doesNothing() {
+        // Set task as systemUI package
+        val systemUIPackageName =
+            context.resources.getString(com.android.internal.R.string.config_systemUi)
+        val baseComponent = ComponentName(systemUIPackageName, /* class */ "")
+        val task =
+            setUpFullscreenTask().apply {
+                baseActivity = baseComponent
+                isTopActivityNoDisplay = false
+            }
+
+        controller.moveRunningTaskToDesktop(task, transitionSource = UNKNOWN)
+        verifyEnterDesktopWCTNotExecuted()
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
+    fun moveRunningTaskToDesktop_systemUIActivityWithoutDisplay_doesNothing() {
+        // Set task as systemUI package
+        val systemUIPackageName =
+            context.resources.getString(com.android.internal.R.string.config_systemUi)
+        val baseComponent = ComponentName(systemUIPackageName, /* class */ "")
+        val task =
+            setUpFullscreenTask().apply {
+                baseActivity = baseComponent
+                isTopActivityNoDisplay = true
+            }
+
+        controller.moveRunningTaskToDesktop(task, transitionSource = UNKNOWN)
+
+        val wct = getLatestEnterDesktopWct()
+        assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
+            .isEqualTo(WINDOWING_MODE_FREEFORM)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+    fun moveBackgroundTaskToDesktop_remoteTransition_usesOneShotHandler() {
+        val transitionHandlerArgCaptor = ArgumentCaptor.forClass(TransitionHandler::class.java)
+        whenever(transitions.startTransition(anyInt(), any(), transitionHandlerArgCaptor.capture()))
+            .thenReturn(Binder())
+
+        val task = createTaskInfo(1)
+        whenever(shellTaskOrganizer.getRunningTaskInfo(anyInt())).thenReturn(null)
+        whenever(recentTasksController.findTaskInBackground(anyInt())).thenReturn(task)
+        controller.moveTaskToDesktop(
+            taskId = task.taskId,
+            transitionSource = UNKNOWN,
+            remoteTransition = RemoteTransition(spy(TestRemoteTransition())),
+        )
+
+        verify(desktopModeEnterExitTransitionListener)
+            .onEnterDesktopModeTransitionStarted(FREEFORM_ANIMATION_DURATION)
+        assertIs<OneShotRemoteHandler>(transitionHandlerArgCaptor.value)
+    }
+
+    @Test
+    fun moveRunningTaskToDesktop_remoteTransition_usesOneShotHandler() {
+        val transitionHandlerArgCaptor = ArgumentCaptor.forClass(TransitionHandler::class.java)
+        whenever(transitions.startTransition(anyInt(), any(), transitionHandlerArgCaptor.capture()))
+            .thenReturn(Binder())
+
+        controller.moveRunningTaskToDesktop(
+            task = setUpFullscreenTask(),
+            transitionSource = UNKNOWN,
+            remoteTransition = RemoteTransition(spy(TestRemoteTransition())),
+        )
+
+        verify(desktopModeEnterExitTransitionListener)
+            .onEnterDesktopModeTransitionStarted(FREEFORM_ANIMATION_DURATION)
+        assertIs<OneShotRemoteHandler>(transitionHandlerArgCaptor.value)
+    }
+
+    @Test
+    @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+    fun moveRunningTaskToDesktop_otherFreeformTasksBroughtToFront_desktopWallpaperDisabled() {
+        val homeTask = setUpHomeTask()
+        val freeformTask = setUpFreeformTask()
+        val fullscreenTask = setUpFullscreenTask()
+        markTaskHidden(freeformTask)
+
+        controller.moveRunningTaskToDesktop(fullscreenTask, transitionSource = UNKNOWN)
+
+        with(getLatestEnterDesktopWct()) {
+            // Operations should include home task, freeform task
+            assertThat(hierarchyOps).hasSize(3)
+            assertReorderSequence(homeTask, freeformTask, fullscreenTask)
+            assertThat(changes[fullscreenTask.token.asBinder()]?.windowingMode)
+                .isEqualTo(WINDOWING_MODE_FREEFORM)
+        }
+        verify(desktopModeEnterExitTransitionListener)
+            .onEnterDesktopModeTransitionStarted(FREEFORM_ANIMATION_DURATION)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+    fun moveRunningTaskToDesktop_otherFreeformTasksBroughtToFront_desktopWallpaperEnabled() {
+        val freeformTask = setUpFreeformTask()
+        val fullscreenTask = setUpFullscreenTask()
+        markTaskHidden(freeformTask)
+
+        controller.moveRunningTaskToDesktop(fullscreenTask, transitionSource = UNKNOWN)
+
+        with(getLatestEnterDesktopWct()) {
+            // Operations should include wallpaper intent, freeform task, fullscreen task
+            assertThat(hierarchyOps).hasSize(3)
+            assertPendingIntentAt(index = 0, desktopWallpaperIntent)
+            assertReorderAt(index = 1, freeformTask)
+            assertReorderAt(index = 2, fullscreenTask)
+            assertThat(changes[fullscreenTask.token.asBinder()]?.windowingMode)
+                .isEqualTo(WINDOWING_MODE_FREEFORM)
+        }
+        verify(desktopModeEnterExitTransitionListener)
+            .onEnterDesktopModeTransitionStarted(FREEFORM_ANIMATION_DURATION)
+    }
+
+    @Test
+    fun moveRunningTaskToDesktop_onlyFreeformTasksFromCurrentDisplayBroughtToFront() {
+        setUpHomeTask(displayId = DEFAULT_DISPLAY)
+        val freeformTaskDefault = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
+        val fullscreenTaskDefault = setUpFullscreenTask(displayId = DEFAULT_DISPLAY)
+        markTaskHidden(freeformTaskDefault)
+
+        val homeTaskSecond = setUpHomeTask(displayId = SECOND_DISPLAY)
+        val freeformTaskSecond = setUpFreeformTask(displayId = SECOND_DISPLAY)
+        markTaskHidden(freeformTaskSecond)
+
+        controller.moveRunningTaskToDesktop(fullscreenTaskDefault, transitionSource = UNKNOWN)
+
+        with(getLatestEnterDesktopWct()) {
+            // Check that hierarchy operations do not include tasks from second display
+            assertThat(hierarchyOps.map { it.container })
+                .doesNotContain(homeTaskSecond.token.asBinder())
+            assertThat(hierarchyOps.map { it.container })
+                .doesNotContain(freeformTaskSecond.token.asBinder())
+        }
+        verify(desktopModeEnterExitTransitionListener)
+            .onEnterDesktopModeTransitionStarted(FREEFORM_ANIMATION_DURATION)
+    }
+
+    @Test
+    fun moveRunningTaskToDesktop_splitTaskExitsSplit() {
+        val task = setUpSplitScreenTask()
+        controller.moveRunningTaskToDesktop(task, transitionSource = UNKNOWN)
+        val wct = getLatestEnterDesktopWct()
+        assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
+            .isEqualTo(WINDOWING_MODE_FREEFORM)
+        verify(desktopModeEnterExitTransitionListener)
+            .onEnterDesktopModeTransitionStarted(FREEFORM_ANIMATION_DURATION)
+        verify(splitScreenController)
+            .prepareExitSplitScreen(
+                any(),
+                anyInt(),
+                eq(SplitScreenController.EXIT_REASON_DESKTOP_MODE),
+            )
+    }
+
+    @Test
+    fun moveRunningTaskToDesktop_fullscreenTaskDoesNotExitSplit() {
+        val task = setUpFullscreenTask()
+        controller.moveRunningTaskToDesktop(task, transitionSource = UNKNOWN)
+        val wct = getLatestEnterDesktopWct()
+        assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
+            .isEqualTo(WINDOWING_MODE_FREEFORM)
+        verify(desktopModeEnterExitTransitionListener)
+            .onEnterDesktopModeTransitionStarted(FREEFORM_ANIMATION_DURATION)
+        verify(splitScreenController, never())
+            .prepareExitSplitScreen(
+                any(),
+                anyInt(),
+                eq(SplitScreenController.EXIT_REASON_DESKTOP_MODE),
+            )
+    }
+
+    @Test
+    @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+    fun moveRunningTaskToDesktop_desktopWallpaperDisabled_bringsTasksOver_dontShowBackTask() {
+        val freeformTasks = (1..MAX_TASK_LIMIT).map { _ -> setUpFreeformTask() }
+        val newTask = setUpFullscreenTask()
+        val homeTask = setUpHomeTask()
+
+        controller.moveRunningTaskToDesktop(newTask, transitionSource = UNKNOWN)
+
+        val wct = getLatestEnterDesktopWct()
+        verify(desktopModeEnterExitTransitionListener)
+            .onEnterDesktopModeTransitionStarted(FREEFORM_ANIMATION_DURATION)
+        assertThat(wct.hierarchyOps.size).isEqualTo(MAX_TASK_LIMIT + 1) // visible tasks + home
+        wct.assertReorderAt(0, homeTask)
+        wct.assertReorderSequenceInRange(
+            range = 1..<(MAX_TASK_LIMIT + 1),
+            *freeformTasks.drop(1).toTypedArray(), // Skipping freeformTasks[0]
+            newTask,
+        )
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+    fun moveRunningTaskToDesktop_desktopWallpaperEnabled_bringsTasksOverLimit_dontShowBackTask() {
+        val freeformTasks = (1..MAX_TASK_LIMIT).map { _ -> setUpFreeformTask() }
+        val newTask = setUpFullscreenTask()
+        val homeTask = setUpHomeTask()
+
+        controller.moveRunningTaskToDesktop(newTask, transitionSource = UNKNOWN)
+
+        val wct = getLatestEnterDesktopWct()
+        verify(desktopModeEnterExitTransitionListener)
+            .onEnterDesktopModeTransitionStarted(FREEFORM_ANIMATION_DURATION)
+        assertThat(wct.hierarchyOps.size).isEqualTo(MAX_TASK_LIMIT + 2) // tasks + home + wallpaper
+        // Move home to front
+        wct.assertReorderAt(0, homeTask)
+        // Add desktop wallpaper activity
+        wct.assertPendingIntentAt(1, desktopWallpaperIntent)
+        // Bring freeform tasks to front
+        wct.assertReorderSequenceInRange(
+            range = 2..<(MAX_TASK_LIMIT + 2),
+            *freeformTasks.drop(1).toTypedArray(), // Skipping freeformTasks[0]
+            newTask,
+        )
+    }
+
+    @Test
+    fun moveToFullscreen_tdaFullscreen_windowingModeSetToUndefined() {
+        val task = setUpFreeformTask()
+        val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!!
+        tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN
+        controller.moveToFullscreen(task.taskId, transitionSource = UNKNOWN)
+        val wct = getLatestExitDesktopWct()
+        verify(desktopModeEnterExitTransitionListener, times(1))
+            .onExitDesktopModeTransitionStarted(FULLSCREEN_ANIMATION_DURATION)
+        assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
+            .isEqualTo(WINDOWING_MODE_UNDEFINED)
+    }
+
+    @Test
+    fun moveToFullscreen_tdaFullscreen_windowingModeUndefined_removesWallpaperActivity() {
+        val task = setUpFreeformTask()
+        val wallpaperToken = MockToken().token()
+
+        taskRepository.wallpaperActivityToken = wallpaperToken
+        assertNotNull(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY))
+            .configuration
+            .windowConfiguration
+            .windowingMode = WINDOWING_MODE_FULLSCREEN
+
+        controller.moveToFullscreen(task.taskId, transitionSource = UNKNOWN)
+
+        val wct = getLatestExitDesktopWct()
+        val taskChange = assertNotNull(wct.changes[task.token.asBinder()])
+        verify(desktopModeEnterExitTransitionListener)
+            .onExitDesktopModeTransitionStarted(FULLSCREEN_ANIMATION_DURATION)
+        assertThat(taskChange.windowingMode).isEqualTo(WINDOWING_MODE_UNDEFINED)
+        // Removes wallpaper activity when leaving desktop
+        wct.assertRemoveAt(index = 0, wallpaperToken)
+    }
+
+    @Test
+    fun moveToFullscreen_tdaFreeform_windowingModeSetToFullscreen() {
+        val task = setUpFreeformTask()
+        val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!!
+        tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM
+        controller.moveToFullscreen(task.taskId, transitionSource = UNKNOWN)
+        val wct = getLatestExitDesktopWct()
+        assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
+            .isEqualTo(WINDOWING_MODE_FULLSCREEN)
+        verify(desktopModeEnterExitTransitionListener)
+            .onExitDesktopModeTransitionStarted(FULLSCREEN_ANIMATION_DURATION)
+    }
+
+    @Test
+    fun moveToFullscreen_tdaFreeform_windowingModeFullscreen_removesWallpaperActivity() {
+        val task = setUpFreeformTask()
+        val wallpaperToken = MockToken().token()
+
+        taskRepository.wallpaperActivityToken = wallpaperToken
+        assertNotNull(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY))
+            .configuration
+            .windowConfiguration
+            .windowingMode = WINDOWING_MODE_FREEFORM
+
+        controller.moveToFullscreen(task.taskId, transitionSource = UNKNOWN)
+
+        val wct = getLatestExitDesktopWct()
+        val taskChange = assertNotNull(wct.changes[task.token.asBinder()])
+        assertThat(taskChange.windowingMode).isEqualTo(WINDOWING_MODE_FULLSCREEN)
+        verify(desktopModeEnterExitTransitionListener)
+            .onExitDesktopModeTransitionStarted(FULLSCREEN_ANIMATION_DURATION)
+        // Removes wallpaper activity when leaving desktop
+        wct.assertRemoveAt(index = 0, wallpaperToken)
+    }
+
+    @Test
+    fun moveToFullscreen_multipleVisibleNonMinimizedTasks_doesNotRemoveWallpaperActivity() {
+        val task1 = setUpFreeformTask()
+        // Setup task2
+        setUpFreeformTask()
+        val wallpaperToken = MockToken().token()
+
+        taskRepository.wallpaperActivityToken = wallpaperToken
+        assertNotNull(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY))
+            .configuration
+            .windowConfiguration
+            .windowingMode = WINDOWING_MODE_FULLSCREEN
+
+        controller.moveToFullscreen(task1.taskId, transitionSource = UNKNOWN)
+
+        val wct = getLatestExitDesktopWct()
+        val task1Change = assertNotNull(wct.changes[task1.token.asBinder()])
+        assertThat(task1Change.windowingMode).isEqualTo(WINDOWING_MODE_UNDEFINED)
+        verify(desktopModeEnterExitTransitionListener)
+            .onExitDesktopModeTransitionStarted(FULLSCREEN_ANIMATION_DURATION)
+        // Does not remove wallpaper activity, as desktop still has a visible desktop task
+        assertThat(wct.hierarchyOps).isEmpty()
+    }
+
+    @Test
+    fun moveToFullscreen_nonExistentTask_doesNothing() {
+        controller.moveToFullscreen(999, transitionSource = UNKNOWN)
+        verifyExitDesktopWCTNotExecuted()
+    }
+
+    @Test
+    fun moveToFullscreen_secondDisplayTaskHasFreeform_secondDisplayNotAffected() {
+        val taskDefaultDisplay = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
+        val taskSecondDisplay = setUpFreeformTask(displayId = SECOND_DISPLAY)
+        controller.moveToFullscreen(taskDefaultDisplay.taskId, transitionSource = UNKNOWN)
+
+        with(getLatestExitDesktopWct()) {
+            assertThat(changes.keys).contains(taskDefaultDisplay.token.asBinder())
+            assertThat(changes.keys).doesNotContain(taskSecondDisplay.token.asBinder())
+        }
+        verify(desktopModeEnterExitTransitionListener)
+            .onExitDesktopModeTransitionStarted(FULLSCREEN_ANIMATION_DURATION)
+    }
+
+    @Test
+    fun moveTaskToFront_postsWctWithReorderOp() {
+        val task1 = setUpFreeformTask()
+        setUpFreeformTask()
+
+        controller.moveTaskToFront(task1, remoteTransition = null)
+
+        val wct = getLatestDesktopMixedTaskWct(type = TRANSIT_TO_FRONT)
+        assertThat(wct.hierarchyOps).hasSize(1)
+        wct.assertReorderAt(index = 0, task1)
+    }
+
+    @Test
+    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 = 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)
+    }
+
+    @Test
+    fun moveTaskToFront_remoteTransition_usesOneshotHandler() {
+        setUpHomeTask()
+        val freeformTasks = List(MAX_TASK_LIMIT) { setUpFreeformTask() }
+        val transitionHandlerArgCaptor = ArgumentCaptor.forClass(TransitionHandler::class.java)
+        whenever(transitions.startTransition(anyInt(), any(), transitionHandlerArgCaptor.capture()))
+            .thenReturn(Binder())
+
+        controller.moveTaskToFront(freeformTasks[0], RemoteTransition(TestRemoteTransition()))
+
+        assertIs<OneShotRemoteHandler>(transitionHandlerArgCaptor.value)
+    }
+
+    @Test
+    fun moveTaskToFront_bringsTasksOverLimit_remoteTransition_usesWindowLimitHandler() {
+        setUpHomeTask()
+        val freeformTasks = List(MAX_TASK_LIMIT + 1) { setUpFreeformTask() }
+        val transitionHandlerArgCaptor = ArgumentCaptor.forClass(TransitionHandler::class.java)
+        whenever(transitions.startTransition(anyInt(), any(), transitionHandlerArgCaptor.capture()))
+            .thenReturn(Binder())
+
+        controller.moveTaskToFront(freeformTasks[0], RemoteTransition(TestRemoteTransition()))
+
+        assertThat(transitionHandlerArgCaptor.value)
+            .isInstanceOf(DesktopWindowLimitRemoteHandler::class.java)
+    }
+
+    @Test
+    fun moveTaskToFront_backgroundTask_launchesTask() {
+        val task = createTaskInfo(1)
+        whenever(shellTaskOrganizer.getRunningTaskInfo(anyInt())).thenReturn(null)
+
+        controller.moveTaskToFront(task.taskId, remoteTransition = null)
+
+        val wct = getLatestDesktopMixedTaskWct(type = TRANSIT_OPEN)
+        assertThat(wct.hierarchyOps).hasSize(1)
+        wct.assertLaunchTaskAt(0, task.taskId, WINDOWING_MODE_FREEFORM)
+    }
+
+    @Test
+    fun moveTaskToFront_backgroundTaskBringsTasksOverLimit_minimizesBackTask() {
+        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 = 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)
+    }
+
+    @Test
+    fun moveToNextDisplay_noOtherDisplays() {
+        whenever(rootTaskDisplayAreaOrganizer.displayIds).thenReturn(intArrayOf(DEFAULT_DISPLAY))
+        val task = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
+        controller.moveToNextDisplay(task.taskId)
+        verifyWCTNotExecuted()
+    }
+
+    @Test
+    fun moveToNextDisplay_moveFromFirstToSecondDisplay() {
+        // Set up two display ids
+        whenever(rootTaskDisplayAreaOrganizer.displayIds)
+            .thenReturn(intArrayOf(DEFAULT_DISPLAY, SECOND_DISPLAY))
+        // Create a mock for the target display area: second display
+        val secondDisplayArea = DisplayAreaInfo(MockToken().token(), SECOND_DISPLAY, 0)
+        whenever(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(SECOND_DISPLAY))
+            .thenReturn(secondDisplayArea)
+
+        val task = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
+        controller.moveToNextDisplay(task.taskId)
+        with(getLatestWct(type = TRANSIT_CHANGE)) {
+            assertThat(hierarchyOps).hasSize(1)
+            assertThat(hierarchyOps[0].container).isEqualTo(task.token.asBinder())
+            assertThat(hierarchyOps[0].isReparent).isTrue()
+            assertThat(hierarchyOps[0].newParent).isEqualTo(secondDisplayArea.token.asBinder())
+            assertThat(hierarchyOps[0].toTop).isTrue()
+        }
+    }
+
+    @Test
+    fun moveToNextDisplay_moveFromSecondToFirstDisplay() {
+        // Set up two display ids
+        whenever(rootTaskDisplayAreaOrganizer.displayIds)
+            .thenReturn(intArrayOf(DEFAULT_DISPLAY, SECOND_DISPLAY))
+        // Create a mock for the target display area: default display
+        val defaultDisplayArea = DisplayAreaInfo(MockToken().token(), DEFAULT_DISPLAY, 0)
+        whenever(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY))
+            .thenReturn(defaultDisplayArea)
+
+        val task = setUpFreeformTask(displayId = SECOND_DISPLAY)
+        controller.moveToNextDisplay(task.taskId)
+
+        with(getLatestWct(type = TRANSIT_CHANGE)) {
+            assertThat(hierarchyOps).hasSize(1)
+            assertThat(hierarchyOps[0].container).isEqualTo(task.token.asBinder())
+            assertThat(hierarchyOps[0].isReparent).isTrue()
+            assertThat(hierarchyOps[0].newParent).isEqualTo(defaultDisplayArea.token.asBinder())
+            assertThat(hierarchyOps[0].toTop).isTrue()
+        }
+    }
+
+    @Test
+    @EnableFlags(FLAG_ENABLE_PER_DISPLAY_DESKTOP_WALLPAPER_ACTIVITY)
+    fun moveToNextDisplay_removeWallpaper() {
+        // Set up two display ids
+        whenever(rootTaskDisplayAreaOrganizer.displayIds)
+            .thenReturn(intArrayOf(DEFAULT_DISPLAY, SECOND_DISPLAY))
+        // Create a mock for the target display area: second display
+        val secondDisplayArea = DisplayAreaInfo(MockToken().token(), SECOND_DISPLAY, 0)
+        whenever(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(SECOND_DISPLAY))
+            .thenReturn(secondDisplayArea)
+        // Add a task and a wallpaper
+        val task = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
+        val wallpaperToken = MockToken().token()
+        taskRepository.wallpaperActivityToken = wallpaperToken
+
+        controller.moveToNextDisplay(task.taskId)
+
+        with(getLatestWct(type = TRANSIT_CHANGE)) {
+            val wallpaperChange =
+                hierarchyOps.find { op -> op.container == wallpaperToken.asBinder() }
+            assertThat(wallpaperChange).isNotNull()
+            assertThat(wallpaperChange!!.type).isEqualTo(HIERARCHY_OP_TYPE_REMOVE_TASK)
+        }
+    }
+
+    @Test
+    @EnableFlags(FLAG_ENABLE_MOVE_TO_NEXT_DISPLAY_SHORTCUT)
+    fun moveToNextDisplay_sizeInDpPreserved() {
+        // Set up two display ids
+        whenever(rootTaskDisplayAreaOrganizer.displayIds)
+            .thenReturn(intArrayOf(DEFAULT_DISPLAY, SECOND_DISPLAY))
+        // Create a mock for the target display area: second display
+        val secondDisplayArea = DisplayAreaInfo(MockToken().token(), SECOND_DISPLAY, 0)
+        whenever(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(SECOND_DISPLAY))
+            .thenReturn(secondDisplayArea)
+        // Two displays have different density
+        whenever(displayLayout.densityDpi()).thenReturn(320)
+        whenever(displayLayout.width()).thenReturn(2400)
+        whenever(displayLayout.height()).thenReturn(1600)
+        val secondaryLayout = mock(DisplayLayout::class.java)
+        whenever(displayController.getDisplayLayout(SECOND_DISPLAY)).thenReturn(secondaryLayout)
+        whenever(secondaryLayout.densityDpi()).thenReturn(160)
+        whenever(secondaryLayout.width()).thenReturn(1280)
+        whenever(secondaryLayout.height()).thenReturn(720)
+
+        // Place a task with a size of 640x480 at a position where the ratio of the left margin to
+        // the right margin is 1:3 and the ratio of top margin to the bottom margin is 1:2.
+        val task =
+            setUpFreeformTask(displayId = DEFAULT_DISPLAY, bounds = Rect(440, 374, 1080, 854))
+
+        controller.moveToNextDisplay(task.taskId)
+
+        with(getLatestWct(type = TRANSIT_CHANGE)) {
+            val taskChange = changes[task.token.asBinder()]
+            assertThat(taskChange).isNotNull()
+            // To preserve DP size, pixel size is changed to 320x240. The ratio of the left margin
+            // to the right margin and the ratio of the top margin to bottom margin are also
+            // preserved.
+            assertThat(taskChange!!.configuration.windowConfiguration.bounds)
+                .isEqualTo(Rect(240, 160, 560, 400))
+        }
+    }
+
+    @Test
+    @EnableFlags(FLAG_ENABLE_MOVE_TO_NEXT_DISPLAY_SHORTCUT)
+    fun moveToNextDisplay_shiftWithinDestinationDisplayBounds() {
+        // Set up two display ids
+        whenever(rootTaskDisplayAreaOrganizer.displayIds)
+            .thenReturn(intArrayOf(DEFAULT_DISPLAY, SECOND_DISPLAY))
+        // Create a mock for the target display area: second display
+        val secondDisplayArea = DisplayAreaInfo(MockToken().token(), SECOND_DISPLAY, 0)
+        whenever(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(SECOND_DISPLAY))
+            .thenReturn(secondDisplayArea)
+        // Two displays have different density
+        whenever(displayLayout.densityDpi()).thenReturn(320)
+        whenever(displayLayout.width()).thenReturn(2400)
+        whenever(displayLayout.height()).thenReturn(1600)
+        val secondaryLayout = mock(DisplayLayout::class.java)
+        whenever(displayController.getDisplayLayout(SECOND_DISPLAY)).thenReturn(secondaryLayout)
+        whenever(secondaryLayout.densityDpi()).thenReturn(160)
+        whenever(secondaryLayout.width()).thenReturn(1280)
+        whenever(secondaryLayout.height()).thenReturn(720)
+
+        // Place a task with a size of 640x480 at a position where the bottom-right corner of the
+        // window is outside the source display bounds. The destination display still has enough
+        // space to place the window within its bounds.
+        val task =
+            setUpFreeformTask(displayId = DEFAULT_DISPLAY, bounds = Rect(2000, 1200, 2640, 1680))
+
+        controller.moveToNextDisplay(task.taskId)
+
+        with(getLatestWct(type = TRANSIT_CHANGE)) {
+            val taskChange = changes[task.token.asBinder()]
+            assertThat(taskChange).isNotNull()
+            assertThat(taskChange!!.configuration.windowConfiguration.bounds)
+                .isEqualTo(Rect(960, 480, 1280, 720))
+        }
+    }
+
+    @Test
+    @EnableFlags(FLAG_ENABLE_MOVE_TO_NEXT_DISPLAY_SHORTCUT)
+    fun moveToNextDisplay_maximizedTask() {
+        // Set up two display ids
+        whenever(rootTaskDisplayAreaOrganizer.displayIds)
+            .thenReturn(intArrayOf(DEFAULT_DISPLAY, SECOND_DISPLAY))
+        // Create a mock for the target display area: second display
+        val secondDisplayArea = DisplayAreaInfo(MockToken().token(), SECOND_DISPLAY, 0)
+        whenever(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(SECOND_DISPLAY))
+            .thenReturn(secondDisplayArea)
+        // Two displays have different density
+        whenever(displayLayout.densityDpi()).thenReturn(320)
+        whenever(displayLayout.width()).thenReturn(1280)
+        whenever(displayLayout.height()).thenReturn(960)
+        val secondaryLayout = mock(DisplayLayout::class.java)
+        whenever(displayController.getDisplayLayout(SECOND_DISPLAY)).thenReturn(secondaryLayout)
+        whenever(secondaryLayout.densityDpi()).thenReturn(160)
+        whenever(secondaryLayout.width()).thenReturn(1280)
+        whenever(secondaryLayout.height()).thenReturn(720)
+
+        // Place a task with a size equals to display size.
+        val task = setUpFreeformTask(displayId = DEFAULT_DISPLAY, bounds = Rect(0, 0, 1280, 960))
+
+        controller.moveToNextDisplay(task.taskId)
+
+        with(getLatestWct(type = TRANSIT_CHANGE)) {
+            val taskChange = changes[task.token.asBinder()]
+            assertThat(taskChange).isNotNull()
+            // DP size is preserved. The window is centered in the destination display.
+            assertThat(taskChange!!.configuration.windowConfiguration.bounds)
+                .isEqualTo(Rect(320, 120, 960, 600))
+        }
+    }
+
+    @Test
+    @EnableFlags(FLAG_ENABLE_MOVE_TO_NEXT_DISPLAY_SHORTCUT)
+    fun moveToNextDisplay_defaultBoundsWhenDestinationTooSmall() {
+        // Set up two display ids
+        whenever(rootTaskDisplayAreaOrganizer.displayIds)
+            .thenReturn(intArrayOf(DEFAULT_DISPLAY, SECOND_DISPLAY))
+        // Create a mock for the target display area: second display
+        val secondDisplayArea = DisplayAreaInfo(MockToken().token(), SECOND_DISPLAY, 0)
+        whenever(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(SECOND_DISPLAY))
+            .thenReturn(secondDisplayArea)
+        // Two displays have different density
+        whenever(displayLayout.densityDpi()).thenReturn(320)
+        whenever(displayLayout.width()).thenReturn(2400)
+        whenever(displayLayout.height()).thenReturn(1600)
+        val secondaryLayout = mock(DisplayLayout::class.java)
+        whenever(displayController.getDisplayLayout(SECOND_DISPLAY)).thenReturn(secondaryLayout)
+        whenever(secondaryLayout.densityDpi()).thenReturn(160)
+        whenever(secondaryLayout.width()).thenReturn(640)
+        whenever(secondaryLayout.height()).thenReturn(480)
+        whenever(secondaryLayout.getStableBoundsForDesktopMode(any())).thenAnswer { i ->
+            (i.arguments.first() as Rect).set(0, 0, 640, 480)
+        }
+
+        // A task with a size of 1800x1200 is being placed. To preserve DP size,
+        // 900x600 pixels are needed, which does not fit in the destination display.
+        val task =
+            setUpFreeformTask(displayId = DEFAULT_DISPLAY, bounds = Rect(300, 200, 2100, 1400))
+
+        controller.moveToNextDisplay(task.taskId)
+
+        with(getLatestWct(type = TRANSIT_CHANGE)) {
+            val taskChange = changes[task.token.asBinder()]
+            assertThat(taskChange).isNotNull()
+            assertThat(taskChange!!.configuration.windowConfiguration.bounds.left).isAtLeast(0)
+            assertThat(taskChange.configuration.windowConfiguration.bounds.top).isAtLeast(0)
+            assertThat(taskChange.configuration.windowConfiguration.bounds.right).isAtMost(640)
+            assertThat(taskChange.configuration.windowConfiguration.bounds.bottom).isAtMost(480)
+        }
+    }
+
+    @Test
+    fun getTaskWindowingMode() {
+        val fullscreenTask = setUpFullscreenTask()
+        val freeformTask = setUpFreeformTask()
+
+        assertThat(controller.getTaskWindowingMode(fullscreenTask.taskId))
+            .isEqualTo(WINDOWING_MODE_FULLSCREEN)
+        assertThat(controller.getTaskWindowingMode(freeformTask.taskId))
+            .isEqualTo(WINDOWING_MODE_FREEFORM)
+        assertThat(controller.getTaskWindowingMode(999)).isEqualTo(WINDOWING_MODE_UNDEFINED)
+    }
+
+    @Test
+    fun onDesktopWindowClose_noActiveTasks() {
+        val task = setUpFreeformTask(active = false)
+        val wct = WindowContainerTransaction()
+        controller.onDesktopWindowClose(wct, displayId = DEFAULT_DISPLAY, task)
+        // Doesn't modify transaction
+        assertThat(wct.hierarchyOps).isEmpty()
+    }
+
+    @Test
+    fun onDesktopWindowClose_singleActiveTask_noWallpaperActivityToken() {
+        val task = setUpFreeformTask()
+        val wct = WindowContainerTransaction()
+        controller.onDesktopWindowClose(wct, displayId = DEFAULT_DISPLAY, task)
+        // Doesn't modify transaction
+        assertThat(wct.hierarchyOps).isEmpty()
+    }
+
+    @Test
+    fun onDesktopWindowClose_singleActiveTask_hasWallpaperActivityToken() {
+        val task = setUpFreeformTask()
+        val wallpaperToken = MockToken().token()
+        taskRepository.wallpaperActivityToken = wallpaperToken
+
+        val wct = WindowContainerTransaction()
+        controller.onDesktopWindowClose(wct, displayId = DEFAULT_DISPLAY, task)
+        // Adds remove wallpaper operation
+        wct.assertRemoveAt(index = 0, wallpaperToken)
+    }
+
+    @Test
+    fun onDesktopWindowClose_singleActiveTask_isClosing() {
+        val task = setUpFreeformTask()
+        val wallpaperToken = MockToken().token()
+        taskRepository.wallpaperActivityToken = wallpaperToken
+        taskRepository.addClosingTask(DEFAULT_DISPLAY, task.taskId)
+
+        val wct = WindowContainerTransaction()
+        controller.onDesktopWindowClose(wct, displayId = DEFAULT_DISPLAY, task)
+        // Doesn't modify transaction
+        assertThat(wct.hierarchyOps).isEmpty()
+    }
+
+    @Test
+    fun onDesktopWindowClose_singleActiveTask_isMinimized() {
+        val task = setUpFreeformTask()
+        val wallpaperToken = MockToken().token()
+        taskRepository.wallpaperActivityToken = wallpaperToken
+        taskRepository.minimizeTask(DEFAULT_DISPLAY, task.taskId)
+
+        val wct = WindowContainerTransaction()
+        controller.onDesktopWindowClose(wct, displayId = DEFAULT_DISPLAY, task)
+        // Doesn't modify transaction
+        assertThat(wct.hierarchyOps).isEmpty()
+    }
+
+    @Test
+    fun onDesktopWindowClose_multipleActiveTasks() {
+        val task1 = setUpFreeformTask()
+        setUpFreeformTask()
+        val wallpaperToken = MockToken().token()
+        taskRepository.wallpaperActivityToken = wallpaperToken
+
+        val wct = WindowContainerTransaction()
+        controller.onDesktopWindowClose(wct, displayId = DEFAULT_DISPLAY, task1)
+        // Doesn't modify transaction
+        assertThat(wct.hierarchyOps).isEmpty()
+    }
+
+    @Test
+    fun onDesktopWindowClose_multipleActiveTasks_isOnlyNonClosingTask() {
+        val task1 = setUpFreeformTask()
+        val task2 = setUpFreeformTask()
+        val wallpaperToken = MockToken().token()
+        taskRepository.wallpaperActivityToken = wallpaperToken
+        taskRepository.addClosingTask(DEFAULT_DISPLAY, task2.taskId)
+
+        val wct = WindowContainerTransaction()
+        controller.onDesktopWindowClose(wct, displayId = DEFAULT_DISPLAY, task1)
+        // Adds remove wallpaper operation
+        wct.assertRemoveAt(index = 0, wallpaperToken)
+    }
+
+    @Test
+    fun onDesktopWindowClose_multipleActiveTasks_hasMinimized() {
+        val task1 = setUpFreeformTask()
+        val task2 = setUpFreeformTask()
+        val wallpaperToken = MockToken().token()
+        taskRepository.wallpaperActivityToken = wallpaperToken
+        taskRepository.minimizeTask(DEFAULT_DISPLAY, task2.taskId)
+
+        val wct = WindowContainerTransaction()
+        controller.onDesktopWindowClose(wct, displayId = DEFAULT_DISPLAY, task1)
+        // Adds remove wallpaper operation
+        wct.assertRemoveAt(index = 0, wallpaperToken)
+    }
+
+    @Test
+    fun onDesktopWindowMinimize_noActiveTask_doesntRemoveWallpaper() {
+        val task = setUpFreeformTask(active = false)
+        val transition = Binder()
+        whenever(freeformTaskTransitionStarter.startMinimizedModeTransition(any()))
+            .thenReturn(transition)
+        val wallpaperToken = MockToken().token()
+        taskRepository.wallpaperActivityToken = wallpaperToken
+
+        controller.minimizeTask(task)
+
+        val captor = ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
+        verify(freeformTaskTransitionStarter).startMinimizedModeTransition(captor.capture())
+        captor.value.hierarchyOps.none { hop ->
+            hop.type == HIERARCHY_OP_TYPE_REMOVE_TASK && hop.container == wallpaperToken.asBinder()
+        }
+    }
+
+    @Test
+    fun onDesktopWindowMinimize_pipTask_autoEnterEnabled_startPipTransition() {
+        val task = setUpPipTask(autoEnterEnabled = true)
+        val handler = mock(TransitionHandler::class.java)
+        whenever(freeformTaskTransitionStarter.startPipTransition(any())).thenReturn(Binder())
+        whenever(transitions.dispatchRequest(any(), any(), anyOrNull()))
+            .thenReturn(android.util.Pair(handler, WindowContainerTransaction()))
+
+        controller.minimizeTask(task)
+
+        verify(freeformTaskTransitionStarter).startPipTransition(any())
+        verify(freeformTaskTransitionStarter, never()).startMinimizedModeTransition(any())
+    }
+
+    @Test
+    fun onDesktopWindowMinimize_pipTask_autoEnterDisabled_startMinimizeTransition() {
+        val task = setUpPipTask(autoEnterEnabled = false)
+        whenever(freeformTaskTransitionStarter.startMinimizedModeTransition(any()))
+            .thenReturn(Binder())
+
+        controller.minimizeTask(task)
+
+        verify(freeformTaskTransitionStarter).startMinimizedModeTransition(any())
+        verify(freeformTaskTransitionStarter, never()).startPipTransition(any())
+    }
+
+    @Test
+    fun onDesktopWindowMinimize_singleActiveTask_noWallpaperActivityToken_doesntRemoveWallpaper() {
+        val task = setUpFreeformTask(active = true)
+        val transition = Binder()
+        whenever(freeformTaskTransitionStarter.startMinimizedModeTransition(any()))
+            .thenReturn(transition)
+
+        controller.minimizeTask(task)
+
+        val captor = ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
+        verify(freeformTaskTransitionStarter).startMinimizedModeTransition(captor.capture())
+        captor.value.hierarchyOps.none { hop -> hop.type == HIERARCHY_OP_TYPE_REMOVE_TASK }
+    }
+
+    @Test
+    fun onTaskMinimize_singleActiveTask_hasWallpaperActivityToken_removesWallpaper() {
+        val task = setUpFreeformTask()
+        val transition = Binder()
+        whenever(freeformTaskTransitionStarter.startMinimizedModeTransition(any()))
+            .thenReturn(transition)
+        val wallpaperToken = MockToken().token()
+        taskRepository.wallpaperActivityToken = wallpaperToken
+
+        // The only active task is being minimized.
+        controller.minimizeTask(task)
+
+        val captor = ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
+        verify(freeformTaskTransitionStarter).startMinimizedModeTransition(captor.capture())
+        // Adds remove wallpaper operation
+        captor.value.assertRemoveAt(index = 0, wallpaperToken)
+    }
+
+    @Test
+    fun onDesktopWindowMinimize_singleActiveTask_alreadyMinimized_doesntRemoveWallpaper() {
+        val task = setUpFreeformTask()
+        val transition = Binder()
+        whenever(freeformTaskTransitionStarter.startMinimizedModeTransition(any()))
+            .thenReturn(transition)
+        val wallpaperToken = MockToken().token()
+        taskRepository.wallpaperActivityToken = wallpaperToken
+        taskRepository.minimizeTask(DEFAULT_DISPLAY, task.taskId)
+
+        // The only active task is already minimized.
+        controller.minimizeTask(task)
+
+        val captor = ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
+        verify(freeformTaskTransitionStarter).startMinimizedModeTransition(captor.capture())
+        captor.value.hierarchyOps.none { hop ->
+            hop.type == HIERARCHY_OP_TYPE_REMOVE_TASK && hop.container == wallpaperToken.asBinder()
+        }
+    }
+
+    @Test
+    fun onDesktopWindowMinimize_multipleActiveTasks_doesntRemoveWallpaper() {
+        val task1 = setUpFreeformTask(active = true)
+        setUpFreeformTask(active = true)
+        val transition = Binder()
+        whenever(freeformTaskTransitionStarter.startMinimizedModeTransition(any()))
+            .thenReturn(transition)
+        val wallpaperToken = MockToken().token()
+        taskRepository.wallpaperActivityToken = wallpaperToken
+
+        controller.minimizeTask(task1)
+
+        val captor = ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
+        verify(freeformTaskTransitionStarter).startMinimizedModeTransition(captor.capture())
+        captor.value.hierarchyOps.none { hop ->
+            hop.type == HIERARCHY_OP_TYPE_REMOVE_TASK && hop.container == wallpaperToken.asBinder()
+        }
+    }
+
+    @Test
+    fun onDesktopWindowMinimize_multipleActiveTasks_minimizesTheOnlyVisibleTask_removesWallpaper() {
+        val task1 = setUpFreeformTask(active = true)
+        val task2 = setUpFreeformTask(active = true)
+        val transition = Binder()
+        whenever(freeformTaskTransitionStarter.startMinimizedModeTransition(any()))
+            .thenReturn(transition)
+        val wallpaperToken = MockToken().token()
+        taskRepository.wallpaperActivityToken = wallpaperToken
+        taskRepository.minimizeTask(DEFAULT_DISPLAY, task2.taskId)
+
+        // task1 is the only visible task as task2 is minimized.
+        controller.minimizeTask(task1)
+        // Adds remove wallpaper operation
+        val captor = ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
+        verify(freeformTaskTransitionStarter).startMinimizedModeTransition(captor.capture())
+        // Adds remove wallpaper operation
+        captor.value.assertRemoveAt(index = 0, wallpaperToken)
+    }
+
+    @Test
+    fun onDesktopWindowMinimize_triesToExitImmersive() {
+        val task = setUpFreeformTask()
+        val transition = Binder()
+        whenever(freeformTaskTransitionStarter.startMinimizedModeTransition(any()))
+            .thenReturn(transition)
+
+        controller.minimizeTask(task)
+
+        verify(mMockDesktopImmersiveController).exitImmersiveIfApplicable(any(), eq(task), any())
+    }
+
+    @Test
+    fun onDesktopWindowMinimize_invokesImmersiveTransitionStartCallback() {
+        val task = setUpFreeformTask()
+        val transition = Binder()
+        val runOnTransit = RunOnStartTransitionCallback()
+        whenever(freeformTaskTransitionStarter.startMinimizedModeTransition(any()))
+            .thenReturn(transition)
+        whenever(mMockDesktopImmersiveController.exitImmersiveIfApplicable(any(), eq(task), any()))
+            .thenReturn(
+                ExitResult.Exit(exitingTask = task.taskId, runOnTransitionStart = runOnTransit)
+            )
+
+        controller.minimizeTask(task)
+
+        assertThat(runOnTransit.invocations).isEqualTo(1)
+        assertThat(runOnTransit.lastInvoked).isEqualTo(transition)
+    }
+
+    @Test
+    fun handleRequest_fullscreenTask_freeformVisible_returnSwitchToFreeformWCT() {
+        val homeTask = setUpHomeTask()
+        val freeformTask = setUpFreeformTask()
+        markTaskVisible(freeformTask)
+        val fullscreenTask = createFullscreenTask()
+
+        val wct = controller.handleRequest(Binder(), createTransition(fullscreenTask))
+
+        assertNotNull(wct, "should handle request")
+        assertThat(wct.changes[fullscreenTask.token.asBinder()]?.windowingMode)
+            .isEqualTo(WINDOWING_MODE_FREEFORM)
+
+        assertThat(wct.hierarchyOps).hasSize(1)
+    }
+
+    @Test
+    fun handleRequest_fullscreenTaskWithTaskOnHome_freeformVisible_returnSwitchToFreeformWCT() {
+        val homeTask = setUpHomeTask()
+        val freeformTask = setUpFreeformTask()
+        markTaskVisible(freeformTask)
+        val fullscreenTask = createFullscreenTask()
+        fullscreenTask.baseIntent.setFlags(Intent.FLAG_ACTIVITY_TASK_ON_HOME)
+
+        val wct = controller.handleRequest(Binder(), createTransition(fullscreenTask))
+
+        assertNotNull(wct, "should handle request")
+        assertThat(wct.changes[fullscreenTask.token.asBinder()]?.windowingMode)
+            .isEqualTo(WINDOWING_MODE_FREEFORM)
+
+        // There are 5 hops that are happening in this case:
+        // 1. Moving the fullscreen task to top as we add moveToDesktop() changes
+        // 2. Bringing home task to front
+        // 3. Pending intent for the wallpaper
+        // 4. Bringing the existing freeform task to top
+        // 5. Bringing the fullscreen task back at the top
+        assertThat(wct.hierarchyOps).hasSize(5)
+        wct.assertReorderAt(1, homeTask, toTop = true)
+        wct.assertReorderAt(4, fullscreenTask, toTop = true)
+    }
+
+    @Test
+    fun handleRequest_fullscreenTaskToFreeform_underTaskLimit_dontMinimize() {
+        val freeformTask = setUpFreeformTask()
+        markTaskVisible(freeformTask)
+        val fullscreenTask = createFullscreenTask()
+
+        val wct = controller.handleRequest(Binder(), createTransition(fullscreenTask))
+
+        // Make sure we only reorder the new task to top (we don't reorder the old task to bottom)
+        assertThat(wct?.hierarchyOps?.size).isEqualTo(1)
+        wct!!.assertReorderAt(0, fullscreenTask, toTop = true)
+    }
+
+    @Test
+    fun handleRequest_fullscreenTaskToFreeform_bringsTasksOverLimit_otherTaskIsMinimized() {
+        val freeformTasks = (1..MAX_TASK_LIMIT).map { _ -> setUpFreeformTask() }
+        freeformTasks.forEach { markTaskVisible(it) }
+        val fullscreenTask = createFullscreenTask()
+
+        val wct = controller.handleRequest(Binder(), createTransition(fullscreenTask))
+
+        // Make sure we reorder the new task to top, and the back task to the bottom
+        assertThat(wct!!.hierarchyOps.size).isEqualTo(2)
+        wct.assertReorderAt(0, fullscreenTask, toTop = true)
+        wct.assertReorderAt(1, freeformTasks[0], toTop = false)
+    }
+
+    @Test
+    fun handleRequest_fullscreenTaskWithTaskOnHome_bringsTasksOverLimit_otherTaskIsMinimized() {
+        val freeformTasks = (1..MAX_TASK_LIMIT).map { _ -> setUpFreeformTask() }
+        freeformTasks.forEach { markTaskVisible(it) }
+        val fullscreenTask = createFullscreenTask()
+        fullscreenTask.baseIntent.setFlags(Intent.FLAG_ACTIVITY_TASK_ON_HOME)
+
+        val wct = controller.handleRequest(Binder(), createTransition(fullscreenTask))
+
+        // Make sure we reorder the new task to top, and the back task to the bottom
+        assertThat(wct!!.hierarchyOps.size).isEqualTo(9)
+        wct.assertReorderAt(0, fullscreenTask, toTop = true)
+        wct.assertReorderAt(8, freeformTasks[0], toTop = false)
+    }
+
+    @Test
+    fun handleRequest_fullscreenTaskWithTaskOnHome_beyondLimit_existingAndNewTasksAreMinimized() {
+        val minimizedTask = setUpFreeformTask()
+        taskRepository.minimizeTask(displayId = DEFAULT_DISPLAY, taskId = minimizedTask.taskId)
+        val freeformTasks = (1..MAX_TASK_LIMIT).map { _ -> setUpFreeformTask() }
+        freeformTasks.forEach { markTaskVisible(it) }
+        val homeTask = setUpHomeTask()
+        val fullscreenTask = createFullscreenTask()
+        fullscreenTask.baseIntent.setFlags(Intent.FLAG_ACTIVITY_TASK_ON_HOME)
+
+        val wct = controller.handleRequest(Binder(), createTransition(fullscreenTask))
+
+        assertThat(wct!!.hierarchyOps.size).isEqualTo(10)
+        wct.assertReorderAt(0, fullscreenTask, toTop = true)
+        // Make sure we reorder the home task to the top, desktop tasks to top of them and minimized
+        // task is under the home task.
+        wct.assertReorderAt(1, homeTask, toTop = true)
+        wct.assertReorderAt(9, freeformTasks[0], toTop = false)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+    fun handleRequest_fullscreenTask_noTasks_enforceDesktop_freeformDisplay_returnFreeformWCT() {
+        whenever(DesktopModeStatus.enterDesktopByDefaultOnFreeformDisplay(context)).thenReturn(true)
+        val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!!
+        tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM
+
+        val fullscreenTask = createFullscreenTask()
+        val wct = controller.handleRequest(Binder(), createTransition(fullscreenTask))
+
+        assertNotNull(wct, "should handle request")
+        assertThat(wct.changes[fullscreenTask.token.asBinder()]?.windowingMode)
+            .isEqualTo(WINDOWING_MODE_UNDEFINED)
+        assertThat(wct.hierarchyOps).hasSize(3)
+        // There are 3 hops that are happening in this case:
+        // 1. Moving the fullscreen task to top as we add moveToDesktop() changes
+        // 2. Pending intent for the wallpaper
+        // 3. Bringing the fullscreen task back at the top
+        wct.assertPendingIntentAt(1, desktopWallpaperIntent)
+        wct.assertReorderAt(2, fullscreenTask, toTop = true)
+    }
+
+    @Test
+    fun handleRequest_fullscreenTask_noTasks_enforceDesktop_fullscreenDisplay_returnNull() {
+        whenever(DesktopModeStatus.enterDesktopByDefaultOnFreeformDisplay(context)).thenReturn(true)
+        val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!!
+        tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN
+
+        val fullscreenTask = createFullscreenTask()
+        val wct = controller.handleRequest(Binder(), createTransition(fullscreenTask))
+
+        assertThat(wct).isNull()
+    }
+
+    @Test
+    fun handleRequest_fullscreenTask_freeformNotVisible_returnNull() {
+        val freeformTask = setUpFreeformTask()
+        markTaskHidden(freeformTask)
+        val fullscreenTask = createFullscreenTask()
+        assertThat(controller.handleRequest(Binder(), createTransition(fullscreenTask))).isNull()
+    }
+
+    @Test
+    fun handleRequest_fullscreenTask_noOtherTasks_returnNull() {
+        val fullscreenTask = createFullscreenTask()
+        assertThat(controller.handleRequest(Binder(), createTransition(fullscreenTask))).isNull()
+    }
+
+    @Test
+    fun handleRequest_fullscreenTask_freeformTaskOnOtherDisplay_returnNull() {
+        val fullscreenTaskDefaultDisplay = createFullscreenTask(displayId = DEFAULT_DISPLAY)
+        createFreeformTask(displayId = SECOND_DISPLAY)
+
+        val result =
+            controller.handleRequest(Binder(), createTransition(fullscreenTaskDefaultDisplay))
+        assertThat(result).isNull()
+    }
+
+    @Test
+    fun handleRequest_freeformTask_freeformVisible_aboveTaskLimit_minimize() {
+        val freeformTasks = (1..MAX_TASK_LIMIT).map { _ -> setUpFreeformTask() }
+        freeformTasks.forEach { markTaskVisible(it) }
+        val newFreeformTask = createFreeformTask()
+
+        val wct =
+            controller.handleRequest(Binder(), createTransition(newFreeformTask, TRANSIT_OPEN))
+
+        assertThat(wct?.hierarchyOps?.size).isEqualTo(1)
+        wct!!.assertReorderAt(0, freeformTasks[0], toTop = false) // Reorder to the bottom
+    }
+
+    @Test
+    fun handleRequest_freeformTask_relaunchActiveTask_taskBecomesUndefined() {
+        val freeformTask = setUpFreeformTask()
+        markTaskHidden(freeformTask)
+
+        val wct = controller.handleRequest(Binder(), createTransition(freeformTask))
+
+        // Should become undefined as the TDA is set to fullscreen. It will inherit from the TDA.
+        assertNotNull(wct, "should handle request")
+        assertThat(wct.changes[freeformTask.token.asBinder()]?.windowingMode)
+            .isEqualTo(WINDOWING_MODE_UNDEFINED)
+    }
+
+    @Test
+    fun handleRequest_freeformTask_relaunchTask_enforceDesktop_freeformDisplay_noWinModeChange() {
+        whenever(DesktopModeStatus.enterDesktopByDefaultOnFreeformDisplay(context)).thenReturn(true)
+        val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!!
+        tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM
+
+        val freeformTask = setUpFreeformTask()
+        markTaskHidden(freeformTask)
+        val wct = controller.handleRequest(Binder(), createTransition(freeformTask))
+
+        assertNotNull(wct, "should handle request")
+        assertFalse(wct.anyWindowingModeChange(freeformTask.token))
+    }
+
+    @Test
+    fun handleRequest_freeformTask_relaunchTask_enforceDesktop_fullscreenDisplay_becomesUndefined() {
+        whenever(DesktopModeStatus.enterDesktopByDefaultOnFreeformDisplay(context)).thenReturn(true)
+        val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!!
+        tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN
+
+        val freeformTask = setUpFreeformTask()
+        markTaskHidden(freeformTask)
+        val wct = controller.handleRequest(Binder(), createTransition(freeformTask))
+
+        assertNotNull(wct, "should handle request")
+        assertThat(wct.changes[freeformTask.token.asBinder()]?.windowingMode)
+            .isEqualTo(WINDOWING_MODE_UNDEFINED)
+    }
+
+    @Test
+    @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+    fun handleRequest_freeformTask_desktopWallpaperDisabled_freeformNotVisible_reorderedToTop() {
+        val freeformTask1 = setUpFreeformTask()
+        val freeformTask2 = createFreeformTask()
+
+        markTaskHidden(freeformTask1)
+        val result =
+            controller.handleRequest(
+                Binder(),
+                createTransition(freeformTask2, type = TRANSIT_TO_FRONT),
+            )
+
+        assertNotNull(result, "Should handle request")
+        assertThat(result.hierarchyOps?.size).isEqualTo(2)
+        result.assertReorderAt(1, freeformTask2, toTop = true)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+    fun handleRequest_freeformTask_desktopWallpaperEnabled_freeformNotVisible_reorderedToTop() {
+        val freeformTask1 = setUpFreeformTask()
+        val freeformTask2 = createFreeformTask()
+
+        markTaskHidden(freeformTask1)
+        val result =
+            controller.handleRequest(
+                Binder(),
+                createTransition(freeformTask2, type = TRANSIT_TO_FRONT),
+            )
+
+        assertNotNull(result, "Should handle request")
+        assertThat(result.hierarchyOps?.size).isEqualTo(3)
+        // Add desktop wallpaper activity
+        result.assertPendingIntentAt(0, desktopWallpaperIntent)
+        // Bring active desktop tasks to front
+        result.assertReorderAt(1, freeformTask1, toTop = true)
+        // Bring new task to front
+        result.assertReorderAt(2, freeformTask2, toTop = true)
+    }
+
+    @Test
+    @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+    fun handleRequest_freeformTask_desktopWallpaperDisabled_noOtherTasks_reorderedToTop() {
+        val task = createFreeformTask()
+        val result = controller.handleRequest(Binder(), createTransition(task))
+
+        assertNotNull(result, "Should handle request")
+        assertThat(result.hierarchyOps?.size).isEqualTo(1)
+        result.assertReorderAt(0, task, toTop = true)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+    fun handleRequest_freeformTask_desktopWallpaperEnabled_noOtherTasks_reorderedToTop() {
+        val task = createFreeformTask()
+        val result = controller.handleRequest(Binder(), createTransition(task))
+
+        assertNotNull(result, "Should handle request")
+        assertThat(result.hierarchyOps?.size).isEqualTo(2)
+        // Add desktop wallpaper activity
+        result.assertPendingIntentAt(0, desktopWallpaperIntent)
+        // Bring new task to front
+        result.assertReorderAt(1, task, toTop = true)
+    }
+
+    @Test
+    @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+    fun handleRequest_freeformTask_dskWallpaperDisabled_freeformOnOtherDisplayOnly_reorderedToTop() {
+        val taskDefaultDisplay = createFreeformTask(displayId = DEFAULT_DISPLAY)
+        // Second display task
+        createFreeformTask(displayId = SECOND_DISPLAY)
+
+        val result = controller.handleRequest(Binder(), createTransition(taskDefaultDisplay))
+
+        assertNotNull(result, "Should handle request")
+        assertThat(result.hierarchyOps?.size).isEqualTo(1)
+        result.assertReorderAt(0, taskDefaultDisplay, toTop = true)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+    fun handleRequest_freeformTask_dskWallpaperEnabled_freeformOnOtherDisplayOnly_reorderedToTop() {
+        val taskDefaultDisplay = createFreeformTask(displayId = DEFAULT_DISPLAY)
+        // Second display task
+        createFreeformTask(displayId = SECOND_DISPLAY)
+
+        val result = controller.handleRequest(Binder(), createTransition(taskDefaultDisplay))
+
+        assertNotNull(result, "Should handle request")
+        assertThat(result.hierarchyOps?.size).isEqualTo(2)
+        // Add desktop wallpaper activity
+        result.assertPendingIntentAt(0, desktopWallpaperIntent)
+        // Bring new task to front
+        result.assertReorderAt(1, taskDefaultDisplay, toTop = true)
+    }
+
+    @Test
+    fun handleRequest_freeformTask_alreadyInDesktop_noOverrideDensity_noConfigDensityChange() {
+        whenever(DesktopModeStatus.useDesktopOverrideDensity()).thenReturn(false)
+
+        val freeformTask1 = setUpFreeformTask()
+        markTaskVisible(freeformTask1)
+
+        val freeformTask2 = createFreeformTask()
+        val result =
+            controller.handleRequest(
+                freeformTask2.token.asBinder(),
+                createTransition(freeformTask2),
+            )
+        assertFalse(result.anyDensityConfigChange(freeformTask2.token))
+    }
+
+    @Test
+    fun handleRequest_freeformTask_alreadyInDesktop_overrideDensity_hasConfigDensityChange() {
+        whenever(DesktopModeStatus.useDesktopOverrideDensity()).thenReturn(true)
+
+        val freeformTask1 = setUpFreeformTask()
+        markTaskVisible(freeformTask1)
+
+        val freeformTask2 = createFreeformTask()
+        val result =
+            controller.handleRequest(
+                freeformTask2.token.asBinder(),
+                createTransition(freeformTask2),
+            )
+        assertTrue(result.anyDensityConfigChange(freeformTask2.token))
+    }
+
+    @Test
+    fun handleRequest_freeformTask_keyguardLocked_returnNull() {
+        whenever(keyguardManager.isKeyguardLocked).thenReturn(true)
+        val freeformTask = createFreeformTask(displayId = DEFAULT_DISPLAY)
+
+        val result = controller.handleRequest(Binder(), createTransition(freeformTask))
+
+        assertNull(result, "Should NOT handle request")
+    }
+
+    @Test
+    fun handleRequest_notOpenOrToFrontTransition_returnNull() {
+        val task =
+            TestRunningTaskInfoBuilder()
+                .setActivityType(ACTIVITY_TYPE_STANDARD)
+                .setWindowingMode(WINDOWING_MODE_FULLSCREEN)
+                .build()
+        val transition = createTransition(task = task, type = TRANSIT_CLOSE)
+        val result = controller.handleRequest(Binder(), transition)
+        assertThat(result).isNull()
+    }
+
+    @Test
+    fun handleRequest_noTriggerTask_returnNull() {
+        assertThat(controller.handleRequest(Binder(), createTransition(task = null))).isNull()
+    }
+
+    @Test
+    fun handleRequest_triggerTaskNotStandard_returnNull() {
+        val task = TestRunningTaskInfoBuilder().setActivityType(ACTIVITY_TYPE_HOME).build()
+        assertThat(controller.handleRequest(Binder(), createTransition(task))).isNull()
+    }
+
+    @Test
+    fun handleRequest_triggerTaskNotFullscreenOrFreeform_returnNull() {
+        val task =
+            TestRunningTaskInfoBuilder()
+                .setActivityType(ACTIVITY_TYPE_STANDARD)
+                .setWindowingMode(WINDOWING_MODE_MULTI_WINDOW)
+                .build()
+        assertThat(controller.handleRequest(Binder(), createTransition(task))).isNull()
+    }
+
+    @Test
+    fun handleRequest_recentsAnimationRunning_returnNull() {
+        // Set up a visible freeform task so a fullscreen task should be converted to freeform
+        val freeformTask = setUpFreeformTask()
+        markTaskVisible(freeformTask)
+
+        // Mark recents animation running
+        recentsTransitionStateListener.onTransitionStateChanged(TRANSITION_STATE_ANIMATING)
+
+        // Open a fullscreen task, check that it does not result in a WCT with changes to it
+        val fullscreenTask = createFullscreenTask()
+        assertThat(controller.handleRequest(Binder(), createTransition(fullscreenTask))).isNull()
+    }
+
+    @Test
+    fun handleRequest_recentsAnimationRunning_relaunchActiveTask_taskBecomesUndefined() {
+        // Set up a visible freeform task
+        val freeformTask = setUpFreeformTask()
+        markTaskVisible(freeformTask)
+
+        // Mark recents animation running
+        recentsTransitionStateListener.onTransitionStateChanged(TRANSITION_STATE_ANIMATING)
+
+        // Should become undefined as the TDA is set to fullscreen. It will inherit from the TDA.
+        val result = controller.handleRequest(Binder(), createTransition(freeformTask))
+        assertThat(result?.changes?.get(freeformTask.token.asBinder())?.windowingMode)
+            .isEqualTo(WINDOWING_MODE_UNDEFINED)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
+    fun handleRequest_topActivityTransparentWithoutDisplay_returnSwitchToFreeformWCT() {
+        val freeformTask = setUpFreeformTask()
+        markTaskVisible(freeformTask)
+
+        val task =
+            setUpFullscreenTask().apply {
+                isActivityStackTransparent = true
+                isTopActivityNoDisplay = true
+                numActivities = 1
+            }
+
+        val result = controller.handleRequest(Binder(), createTransition(task))
+        assertThat(result?.changes?.get(task.token.asBinder())?.windowingMode)
+            .isEqualTo(WINDOWING_MODE_FREEFORM)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
+    fun handleRequest_topActivityTransparentWithDisplay_returnSwitchToFullscreenWCT() {
+        val freeformTask = setUpFreeformTask()
+        markTaskVisible(freeformTask)
+
+        val task =
+            setUpFreeformTask().apply {
+                isActivityStackTransparent = true
+                isTopActivityNoDisplay = false
+                numActivities = 1
+            }
+
+        val result = controller.handleRequest(Binder(), createTransition(task))
+        assertThat(result?.changes?.get(task.token.asBinder())?.windowingMode)
+            .isEqualTo(WINDOWING_MODE_UNDEFINED) // inherited FULLSCREEN
+    }
+
+    @Test
+    @EnableFlags(
+        Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY,
+        Flags.FLAG_INCLUDE_TOP_TRANSPARENT_FULLSCREEN_TASK_IN_DESKTOP_HEURISTIC,
     )
-    whenever(displayLayout.getStableBoundsForDesktopMode(any())).thenAnswer { i ->
-      (i.arguments.first() as Rect).set(stableBounds)
+    fun handleRequest_topActivityTransparentWithDisplay_savedToDesktopRepository() {
+        val freeformTask = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
+        markTaskVisible(freeformTask)
+
+        val transparentTask =
+            setUpFreeformTask(displayId = DEFAULT_DISPLAY).apply {
+                isActivityStackTransparent = true
+                isTopActivityNoDisplay = false
+                numActivities = 1
+            }
+
+        controller.handleRequest(Binder(), createTransition(transparentTask))
+        assertThat(taskRepository.getTopTransparentFullscreenTaskId(DEFAULT_DISPLAY))
+            .isEqualTo(transparentTask.taskId)
     }
-  }
 
-  private fun setUpSplitScreenTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo {
-    val task = createSplitScreenTask(displayId)
-    whenever(splitScreenController.isTaskInSplitScreen(task.taskId)).thenReturn(true)
-    whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
-    runningTasks.add(task)
-    return task
-  }
+    @Test
+    @EnableFlags(
+        Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY,
+        Flags.FLAG_INCLUDE_TOP_TRANSPARENT_FULLSCREEN_TASK_IN_DESKTOP_HEURISTIC,
+    )
+    fun handleRequest_desktopNotShowing_topTransparentFullscreenTask_notSavedToDesktopRepository() {
+        val task = setUpFullscreenTask(displayId = DEFAULT_DISPLAY)
 
-  private fun markTaskVisible(task: RunningTaskInfo) {
-    taskRepository.updateTask(task.displayId, task.taskId, isVisible = true)
-  }
-
-  private fun markTaskHidden(task: RunningTaskInfo) {
-    taskRepository.updateTask(task.displayId, task.taskId, isVisible = false)
-  }
-
-  private fun getLatestWct(
-      @WindowManager.TransitionType type: Int = TRANSIT_OPEN,
-      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 {
-      verify(transitions).startTransition(eq(type), arg.capture(), isA(handlerClass))
+        controller.handleRequest(Binder(), createTransition(task))
+        assertThat(taskRepository.getTopTransparentFullscreenTaskId(DEFAULT_DISPLAY)).isNull()
     }
-    return arg.value
-  }
 
-  private fun getLatestToggleResizeDesktopTaskWct(
-    currentBounds: Rect? = null
-  ): WindowContainerTransaction {
-    val arg: ArgumentCaptor<WindowContainerTransaction> =
-        ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
-    verify(toggleResizeDesktopTaskTransitionHandler, atLeastOnce())
-        .startTransition(capture(arg), eq(currentBounds))
-    return arg.value
-  }
+    @Test
+    @EnableFlags(
+        Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY,
+        Flags.FLAG_INCLUDE_TOP_TRANSPARENT_FULLSCREEN_TASK_IN_DESKTOP_HEURISTIC,
+    )
+    fun handleRequest_onlyTopTransparentFullscreenTask_returnSwitchToFreeformWCT() {
+        val topTransparentTask = setUpFullscreenTask(displayId = DEFAULT_DISPLAY)
+        taskRepository.setTopTransparentFullscreenTaskId(DEFAULT_DISPLAY, topTransparentTask.taskId)
 
-  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
-  }
+        val task = setUpFullscreenTask(displayId = DEFAULT_DISPLAY)
 
-  private fun getLatestEnterDesktopWct(): WindowContainerTransaction {
-    val arg = ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
-    verify(enterDesktopTransitionHandler).moveToDesktop(arg.capture(), any())
-    return arg.value
-  }
+        val result = controller.handleRequest(Binder(), createTransition(task))
+        assertThat(result?.changes?.get(task.token.asBinder())?.windowingMode)
+            .isEqualTo(WINDOWING_MODE_FREEFORM)
+    }
 
-  private fun getLatestDragToDesktopWct(): WindowContainerTransaction {
-    val arg: ArgumentCaptor<WindowContainerTransaction> =
-        ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
-    verify(dragToDesktopTransitionHandler).finishDragToDesktopTransition(capture(arg))
-    return arg.value
-  }
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
+    fun handleRequest_desktopNotShowing_topTransparentFullscreenTask_returnNull() {
+        val task = setUpFullscreenTask(displayId = DEFAULT_DISPLAY)
 
-  private fun getLatestExitDesktopWct(): WindowContainerTransaction {
-    val arg = ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
-    verify(exitDesktopTransitionHandler).startTransition(any(), arg.capture(), any(), any())
-    return arg.value
-  }
+        assertThat(controller.handleRequest(Binder(), createTransition(task))).isNull()
+    }
 
-  private fun findBoundsChange(wct: WindowContainerTransaction, task: RunningTaskInfo): Rect? =
-      wct.changes[task.token.asBinder()]?.configuration?.windowConfiguration?.bounds
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
+    fun handleRequest_systemUIActivityWithDisplay_returnSwitchToFullscreenWCT() {
+        val freeformTask = setUpFreeformTask()
+        markTaskVisible(freeformTask)
 
-  private fun verifyWCTNotExecuted() {
-    verify(transitions, never()).startTransition(anyInt(), any(), isNull())
-  }
+        // Set task as systemUI package
+        val systemUIPackageName =
+            context.resources.getString(com.android.internal.R.string.config_systemUi)
+        val baseComponent = ComponentName(systemUIPackageName, /* class */ "")
+        val task =
+            setUpFreeformTask().apply {
+                baseActivity = baseComponent
+                isTopActivityNoDisplay = false
+            }
 
-  private fun verifyExitDesktopWCTNotExecuted() {
-    verify(exitDesktopTransitionHandler, never()).startTransition(any(), any(), any(), any())
-  }
+        val result = controller.handleRequest(Binder(), createTransition(task))
+        assertThat(result?.changes?.get(task.token.asBinder())?.windowingMode)
+            .isEqualTo(WINDOWING_MODE_UNDEFINED) // inherited FULLSCREEN
+    }
 
-  private fun verifyEnterDesktopWCTNotExecuted() {
-    verify(enterDesktopTransitionHandler, never()).moveToDesktop(any(), any())
-  }
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
+    fun handleRequest_systemUIActivityWithoutDisplay_returnSwitchToFreeformWCT() {
+        val freeformTask = setUpFreeformTask()
+        markTaskVisible(freeformTask)
 
-  private fun createTransition(
-      task: RunningTaskInfo?,
-      @WindowManager.TransitionType type: Int = TRANSIT_OPEN
-  ): TransitionRequestInfo {
-    return TransitionRequestInfo(type, task, null /* remoteTransition */)
-  }
+        // Set task as systemUI package
+        val systemUIPackageName =
+            context.resources.getString(com.android.internal.R.string.config_systemUi)
+        val baseComponent = ComponentName(systemUIPackageName, /* class */ "")
+        val task =
+            setUpFullscreenTask().apply {
+                baseActivity = baseComponent
+                isTopActivityNoDisplay = true
+            }
 
-  private companion object {
-    const val SECOND_DISPLAY = 2
-    val STABLE_BOUNDS = Rect(0, 0, 1000, 1000)
-    const val MAX_TASK_LIMIT = 6
-    private const val TASKBAR_FRAME_HEIGHT = 200
-  }
+        val result = controller.handleRequest(Binder(), createTransition(task))
+        assertThat(result?.changes?.get(task.token.asBinder())?.windowingMode)
+            .isEqualTo(WINDOWING_MODE_FREEFORM)
+    }
+
+    @Test
+    @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+    fun handleRequest_backTransition_singleTaskNoToken_noWallpaper_doesNotHandle() {
+        val task = setUpFreeformTask()
+
+        val result =
+            controller.handleRequest(Binder(), createTransition(task, type = TRANSIT_TO_BACK))
+
+        assertNull(result, "Should not handle request")
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+    fun handleRequest_backTransition_singleTaskNoToken_withWallpaper_removesTask() {
+        val task = setUpFreeformTask()
+
+        val result =
+            controller.handleRequest(Binder(), createTransition(task, type = TRANSIT_TO_BACK))
+
+        assertNull(result, "Should not handle request")
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+    fun handleRequest_backTransition_singleTaskNoToken_withWallpaper_notInDesktop_doesNotHandle() {
+        val task = setUpFreeformTask()
+        markTaskHidden(task)
+
+        val result =
+            controller.handleRequest(Binder(), createTransition(task, type = TRANSIT_TO_BACK))
+
+        assertNull(result, "Should not handle request")
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+    fun handleRequest_backTransition_singleTaskNoToken_doesNotHandle() {
+        val task = setUpFreeformTask()
+
+        val result =
+            controller.handleRequest(Binder(), createTransition(task, type = TRANSIT_TO_BACK))
+
+        assertNull(result, "Should not handle request")
+    }
+
+    @Test
+    @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+    fun handleRequest_backTransition_singleTaskWithToken_noWallpaper_doesNotHandle() {
+        val task = setUpFreeformTask()
+
+        taskRepository.wallpaperActivityToken = MockToken().token()
+        val result =
+            controller.handleRequest(Binder(), createTransition(task, type = TRANSIT_TO_BACK))
+
+        assertNull(result, "Should not handle request")
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+    fun handleRequest_backTransition_singleTaskWithToken_removesWallpaper() {
+        val task = setUpFreeformTask()
+        val wallpaperToken = MockToken().token()
+
+        taskRepository.wallpaperActivityToken = wallpaperToken
+        val result =
+            controller.handleRequest(Binder(), createTransition(task, type = TRANSIT_TO_BACK))
+
+        // Should create remove wallpaper transaction
+        assertNotNull(result, "Should handle request").assertRemoveAt(index = 0, wallpaperToken)
+    }
+
+    @Test
+    @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+    fun handleRequest_backTransition_multipleTasks_noWallpaper_doesNotHandle() {
+        val task1 = setUpFreeformTask()
+        setUpFreeformTask()
+
+        taskRepository.wallpaperActivityToken = MockToken().token()
+        val result =
+            controller.handleRequest(Binder(), createTransition(task1, type = TRANSIT_TO_BACK))
+
+        assertNull(result, "Should not handle request")
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+    fun handleRequest_backTransition_multipleTasks_doesNotHandle() {
+        val task1 = setUpFreeformTask()
+        setUpFreeformTask()
+
+        taskRepository.wallpaperActivityToken = MockToken().token()
+        val result =
+            controller.handleRequest(Binder(), createTransition(task1, type = TRANSIT_TO_BACK))
+
+        assertNull(result, "Should not handle request")
+    }
+
+    @Test
+    @EnableFlags(
+        Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,
+        Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION,
+    )
+    fun handleRequest_backTransition_multipleTasksSingleNonClosing_removesWallpaperAndTask() {
+        val task1 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
+        val task2 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
+        val wallpaperToken = MockToken().token()
+
+        taskRepository.wallpaperActivityToken = wallpaperToken
+        taskRepository.addClosingTask(displayId = DEFAULT_DISPLAY, taskId = task2.taskId)
+        val result =
+            controller.handleRequest(Binder(), createTransition(task1, type = TRANSIT_TO_BACK))
+
+        // Should create remove wallpaper transaction
+        assertNotNull(result, "Should handle request").assertRemoveAt(index = 0, wallpaperToken)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+    fun handleRequest_backTransition_multipleTasksSingleNonMinimized_removesWallpaperAndTask() {
+        val task1 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
+        val task2 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
+        val wallpaperToken = MockToken().token()
+
+        taskRepository.wallpaperActivityToken = wallpaperToken
+        taskRepository.minimizeTask(displayId = DEFAULT_DISPLAY, taskId = task2.taskId)
+        val result =
+            controller.handleRequest(Binder(), createTransition(task1, type = TRANSIT_TO_BACK))
+
+        // Should create remove wallpaper transaction
+        assertNotNull(result, "Should handle request").assertRemoveAt(index = 0, wallpaperToken)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+    fun handleRequest_backTransition_nonMinimizadTask_withWallpaper_removesWallpaper() {
+        val task1 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
+        val task2 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
+        val wallpaperToken = MockToken().token()
+
+        taskRepository.wallpaperActivityToken = wallpaperToken
+        taskRepository.minimizeTask(displayId = DEFAULT_DISPLAY, taskId = task2.taskId)
+        // Task is being minimized so mark it as not visible.
+        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")
+    }
+
+    @Test
+    @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+    fun handleRequest_closeTransition_singleTaskNoToken_noWallpaper_doesNotHandle() {
+        val task = setUpFreeformTask()
+
+        val result =
+            controller.handleRequest(Binder(), createTransition(task, type = TRANSIT_CLOSE))
+
+        assertNull(result, "Should not handle request")
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+    fun handleRequest_closeTransition_singleTaskNoToken_doesNotHandle() {
+        val task = setUpFreeformTask()
+
+        val result =
+            controller.handleRequest(Binder(), createTransition(task, type = TRANSIT_CLOSE))
+
+        assertNull(result, "Should not handle request")
+    }
+
+    @Test
+    @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+    fun handleRequest_closeTransition_singleTaskWithToken_noWallpaper_doesNotHandle() {
+        val task = setUpFreeformTask()
+
+        taskRepository.wallpaperActivityToken = MockToken().token()
+        val result =
+            controller.handleRequest(Binder(), createTransition(task, type = TRANSIT_CLOSE))
+
+        assertNull(result, "Should not handle request")
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+    fun handleRequest_closeTransition_singleTaskWithToken_withWallpaper_removesWallpaper() {
+        val task = setUpFreeformTask()
+        val wallpaperToken = MockToken().token()
+
+        taskRepository.wallpaperActivityToken = wallpaperToken
+        val result =
+            controller.handleRequest(Binder(), createTransition(task, type = TRANSIT_CLOSE))
+
+        // Should create remove wallpaper transaction
+        assertNotNull(result, "Should handle request").assertRemoveAt(index = 0, wallpaperToken)
+    }
+
+    @Test
+    @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+    fun handleRequest_closeTransition_multipleTasks_noWallpaper_doesNotHandle() {
+        val task1 = setUpFreeformTask()
+        setUpFreeformTask()
+
+        taskRepository.wallpaperActivityToken = MockToken().token()
+        val result =
+            controller.handleRequest(Binder(), createTransition(task1, type = TRANSIT_CLOSE))
+
+        assertNull(result, "Should not handle request")
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+    fun handleRequest_closeTransition_multipleTasksFlagEnabled_doesNotHandle() {
+        val task1 = setUpFreeformTask()
+        setUpFreeformTask()
+
+        taskRepository.wallpaperActivityToken = MockToken().token()
+        val result =
+            controller.handleRequest(Binder(), createTransition(task1, type = TRANSIT_CLOSE))
+
+        assertNull(result, "Should not handle request")
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+    fun handleRequest_closeTransition_multipleTasksSingleNonClosing_removesWallpaper() {
+        val task1 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
+        val task2 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
+        val wallpaperToken = MockToken().token()
+
+        taskRepository.wallpaperActivityToken = wallpaperToken
+        taskRepository.addClosingTask(displayId = DEFAULT_DISPLAY, taskId = task2.taskId)
+        val result =
+            controller.handleRequest(Binder(), createTransition(task1, type = TRANSIT_CLOSE))
+
+        // Should create remove wallpaper transaction
+        assertNotNull(result, "Should handle request").assertRemoveAt(index = 0, wallpaperToken)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+    fun handleRequest_closeTransition_multipleTasksSingleNonMinimized_removesWallpaper() {
+        val task1 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
+        val task2 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
+        val wallpaperToken = MockToken().token()
+
+        taskRepository.wallpaperActivityToken = wallpaperToken
+        taskRepository.minimizeTask(displayId = DEFAULT_DISPLAY, taskId = task2.taskId)
+        val result =
+            controller.handleRequest(Binder(), createTransition(task1, type = TRANSIT_CLOSE))
+
+        // Should create remove wallpaper transaction
+        assertNotNull(result, "Should handle request").assertRemoveAt(index = 0, wallpaperToken)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+    fun handleRequest_closeTransition_minimizadTask_withWallpaper_removesWallpaper() {
+        val task1 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
+        val task2 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
+        val wallpaperToken = MockToken().token()
+
+        taskRepository.wallpaperActivityToken = wallpaperToken
+        taskRepository.minimizeTask(displayId = DEFAULT_DISPLAY, taskId = task2.taskId)
+        // Task is being minimized so mark it as not visible.
+        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")
+    }
+
+    @Test
+    fun moveFocusedTaskToDesktop_fullscreenTaskIsMovedToDesktop() {
+        val task1 = setUpFullscreenTask()
+        val task2 = setUpFullscreenTask()
+        val task3 = setUpFullscreenTask()
+
+        task1.isFocused = true
+        task2.isFocused = false
+        task3.isFocused = false
+
+        controller.moveFocusedTaskToDesktop(DEFAULT_DISPLAY, transitionSource = UNKNOWN)
+
+        val wct = getLatestEnterDesktopWct()
+        assertThat(wct.changes[task1.token.asBinder()]?.windowingMode)
+            .isEqualTo(WINDOWING_MODE_FREEFORM)
+    }
+
+    @Test
+    fun moveFocusedTaskToDesktop_splitScreenTaskIsMovedToDesktop() {
+        val task1 = setUpSplitScreenTask()
+        val task2 = setUpFullscreenTask()
+        val task3 = setUpFullscreenTask()
+        val task4 = setUpSplitScreenTask()
+
+        task1.isFocused = true
+        task2.isFocused = false
+        task3.isFocused = false
+        task4.isFocused = true
+
+        task4.parentTaskId = task1.taskId
+
+        controller.moveFocusedTaskToDesktop(DEFAULT_DISPLAY, transitionSource = UNKNOWN)
+
+        val wct = getLatestEnterDesktopWct()
+        assertThat(wct.changes[task4.token.asBinder()]?.windowingMode)
+            .isEqualTo(WINDOWING_MODE_FREEFORM)
+        verify(splitScreenController)
+            .prepareExitSplitScreen(
+                any(),
+                anyInt(),
+                eq(SplitScreenController.EXIT_REASON_DESKTOP_MODE),
+            )
+    }
+
+    @Test
+    fun moveFocusedTaskToFullscreen() {
+        val task1 = setUpFreeformTask()
+        val task2 = setUpFreeformTask()
+        val task3 = setUpFreeformTask()
+
+        task1.isFocused = false
+        task2.isFocused = true
+        task3.isFocused = false
+
+        controller.enterFullscreen(DEFAULT_DISPLAY, transitionSource = UNKNOWN)
+
+        val wct = getLatestExitDesktopWct()
+        assertThat(wct.changes[task2.token.asBinder()]?.windowingMode)
+            .isEqualTo(WINDOWING_MODE_UNDEFINED) // inherited FULLSCREEN
+    }
+
+    @Test
+    fun moveFocusedTaskToFullscreen_onlyVisibleNonMinimizedTask_removesWallpaperActivity() {
+        val task1 = setUpFreeformTask()
+        val task2 = setUpFreeformTask()
+        val task3 = setUpFreeformTask()
+        val wallpaperToken = MockToken().token()
+
+        task1.isFocused = false
+        task2.isFocused = true
+        task3.isFocused = false
+        taskRepository.wallpaperActivityToken = wallpaperToken
+        taskRepository.minimizeTask(DEFAULT_DISPLAY, task1.taskId)
+        taskRepository.updateTask(DEFAULT_DISPLAY, task3.taskId, isVisible = false)
+
+        controller.enterFullscreen(DEFAULT_DISPLAY, transitionSource = UNKNOWN)
+
+        val wct = getLatestExitDesktopWct()
+        val taskChange = assertNotNull(wct.changes[task2.token.asBinder()])
+        assertThat(taskChange.windowingMode)
+            .isEqualTo(WINDOWING_MODE_UNDEFINED) // inherited FULLSCREEN
+        wct.assertRemoveAt(index = 0, wallpaperToken)
+    }
+
+    @Test
+    fun moveFocusedTaskToFullscreen_multipleVisibleTasks_doesNotRemoveWallpaperActivity() {
+        val task1 = setUpFreeformTask()
+        val task2 = setUpFreeformTask()
+        val task3 = setUpFreeformTask()
+        val wallpaperToken = MockToken().token()
+
+        task1.isFocused = false
+        task2.isFocused = true
+        task3.isFocused = false
+        taskRepository.wallpaperActivityToken = wallpaperToken
+        controller.enterFullscreen(DEFAULT_DISPLAY, transitionSource = UNKNOWN)
+
+        val wct = getLatestExitDesktopWct()
+        val taskChange = assertNotNull(wct.changes[task2.token.asBinder()])
+        assertThat(taskChange.windowingMode)
+            .isEqualTo(WINDOWING_MODE_UNDEFINED) // inherited FULLSCREEN
+        // Does not remove wallpaper activity, as desktop still has visible desktop tasks
+        assertThat(wct.hierarchyOps).isEmpty()
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION)
+    fun removeDesktop_multipleTasks_removesAll() {
+        val task1 = setUpFreeformTask()
+        val task2 = setUpFreeformTask()
+        val task3 = setUpFreeformTask()
+        taskRepository.minimizeTask(DEFAULT_DISPLAY, task2.taskId)
+
+        controller.removeDesktop(displayId = DEFAULT_DISPLAY)
+
+        val wct = getLatestWct(TRANSIT_CLOSE)
+        assertThat(wct.hierarchyOps).hasSize(3)
+        wct.assertRemoveAt(index = 0, task1.token)
+        wct.assertRemoveAt(index = 1, task2.token)
+        wct.assertRemoveAt(index = 2, task3.token)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION)
+    fun removeDesktop_multipleTasksWithBackgroundTask_removesAll() {
+        val task1 = setUpFreeformTask()
+        val task2 = setUpFreeformTask()
+        val task3 = setUpFreeformTask()
+        taskRepository.minimizeTask(DEFAULT_DISPLAY, task2.taskId)
+        whenever(shellTaskOrganizer.getRunningTaskInfo(task3.taskId)).thenReturn(null)
+
+        controller.removeDesktop(displayId = DEFAULT_DISPLAY)
+
+        val wct = getLatestWct(TRANSIT_CLOSE)
+        assertThat(wct.hierarchyOps).hasSize(2)
+        wct.assertRemoveAt(index = 0, task1.token)
+        wct.assertRemoveAt(index = 1, task2.token)
+        verify(recentTasksController).removeBackgroundTask(task3.taskId)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+    fun dragToDesktop_landscapeDevice_resizable_undefinedOrientation_defaultLandscapeBounds() {
+        val spyController = spy(controller)
+        whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
+        whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull()))
+            .thenReturn(DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR)
+
+        val task = setUpFullscreenTask()
+        setUpLandscapeDisplay()
+
+        spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task, mockSurface)
+        val wct = getLatestDragToDesktopWct()
+        assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_LANDSCAPE_BOUNDS)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+    fun dragToDesktop_landscapeDevice_resizable_landscapeOrientation_defaultLandscapeBounds() {
+        val spyController = spy(controller)
+        whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
+        whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull()))
+            .thenReturn(DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR)
+
+        val task = setUpFullscreenTask(screenOrientation = SCREEN_ORIENTATION_LANDSCAPE)
+        setUpLandscapeDisplay()
+
+        spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task, mockSurface)
+        val wct = getLatestDragToDesktopWct()
+        assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_LANDSCAPE_BOUNDS)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+    fun dragToDesktop_landscapeDevice_resizable_portraitOrientation_resizablePortraitBounds() {
+        val spyController = spy(controller)
+        whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
+        whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull()))
+            .thenReturn(DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR)
+
+        val task =
+            setUpFullscreenTask(
+                screenOrientation = SCREEN_ORIENTATION_PORTRAIT,
+                shouldLetterbox = true,
+            )
+        setUpLandscapeDisplay()
+
+        spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task, mockSurface)
+        val wct = getLatestDragToDesktopWct()
+        assertThat(findBoundsChange(wct, task)).isEqualTo(RESIZABLE_PORTRAIT_BOUNDS)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+    fun dragToDesktop_landscapeDevice_unResizable_landscapeOrientation_defaultLandscapeBounds() {
+        val spyController = spy(controller)
+        whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
+        whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull()))
+            .thenReturn(DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR)
+
+        val task =
+            setUpFullscreenTask(
+                isResizable = false,
+                screenOrientation = SCREEN_ORIENTATION_LANDSCAPE,
+            )
+        setUpLandscapeDisplay()
+
+        spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task, mockSurface)
+        val wct = getLatestDragToDesktopWct()
+        assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_LANDSCAPE_BOUNDS)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+    fun dragToDesktop_landscapeDevice_unResizable_portraitOrientation_unResizablePortraitBounds() {
+        val spyController = spy(controller)
+        whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
+        whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull()))
+            .thenReturn(DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR)
+
+        val task =
+            setUpFullscreenTask(
+                isResizable = false,
+                screenOrientation = SCREEN_ORIENTATION_PORTRAIT,
+                shouldLetterbox = true,
+            )
+        setUpLandscapeDisplay()
+
+        spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task, mockSurface)
+        val wct = getLatestDragToDesktopWct()
+        assertThat(findBoundsChange(wct, task)).isEqualTo(UNRESIZABLE_PORTRAIT_BOUNDS)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+    fun dragToDesktop_portraitDevice_resizable_undefinedOrientation_defaultPortraitBounds() {
+        val spyController = spy(controller)
+        whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
+        whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull()))
+            .thenReturn(DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR)
+
+        val task = setUpFullscreenTask(deviceOrientation = ORIENTATION_PORTRAIT)
+        setUpPortraitDisplay()
+
+        spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task, mockSurface)
+        val wct = getLatestDragToDesktopWct()
+        assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_PORTRAIT_BOUNDS)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+    fun dragToDesktop_portraitDevice_resizable_portraitOrientation_defaultPortraitBounds() {
+        val spyController = spy(controller)
+        whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
+        whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull()))
+            .thenReturn(DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR)
+
+        val task =
+            setUpFullscreenTask(
+                deviceOrientation = ORIENTATION_PORTRAIT,
+                screenOrientation = SCREEN_ORIENTATION_PORTRAIT,
+            )
+        setUpPortraitDisplay()
+
+        spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task, mockSurface)
+        val wct = getLatestDragToDesktopWct()
+        assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_PORTRAIT_BOUNDS)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+    fun dragToDesktop_portraitDevice_resizable_landscapeOrientation_resizableLandscapeBounds() {
+        val spyController = spy(controller)
+        whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
+        whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull()))
+            .thenReturn(DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR)
+
+        val task =
+            setUpFullscreenTask(
+                deviceOrientation = ORIENTATION_PORTRAIT,
+                screenOrientation = SCREEN_ORIENTATION_LANDSCAPE,
+                shouldLetterbox = true,
+            )
+        setUpPortraitDisplay()
+
+        spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task, mockSurface)
+        val wct = getLatestDragToDesktopWct()
+        assertThat(findBoundsChange(wct, task)).isEqualTo(RESIZABLE_LANDSCAPE_BOUNDS)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+    fun dragToDesktop_portraitDevice_unResizable_portraitOrientation_defaultPortraitBounds() {
+        val spyController = spy(controller)
+        whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
+        whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull()))
+            .thenReturn(DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR)
+
+        val task =
+            setUpFullscreenTask(
+                isResizable = false,
+                deviceOrientation = ORIENTATION_PORTRAIT,
+                screenOrientation = SCREEN_ORIENTATION_PORTRAIT,
+            )
+        setUpPortraitDisplay()
+
+        spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task, mockSurface)
+        val wct = getLatestDragToDesktopWct()
+        assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_PORTRAIT_BOUNDS)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+    fun dragToDesktop_portraitDevice_unResizable_landscapeOrientation_unResizableLandscapeBounds() {
+        val spyController = spy(controller)
+        whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
+        whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull()))
+            .thenReturn(DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR)
+
+        val task =
+            setUpFullscreenTask(
+                isResizable = false,
+                deviceOrientation = ORIENTATION_PORTRAIT,
+                screenOrientation = SCREEN_ORIENTATION_LANDSCAPE,
+                shouldLetterbox = true,
+            )
+        setUpPortraitDisplay()
+
+        spyController.onDragPositioningEndThroughStatusBar(PointF(200f, 200f), task, mockSurface)
+        val wct = getLatestDragToDesktopWct()
+        assertThat(findBoundsChange(wct, task)).isEqualTo(UNRESIZABLE_LANDSCAPE_BOUNDS)
+    }
+
+    @Test
+    fun onDesktopDragMove_endsOutsideValidDragArea_snapsToValidBounds() {
+        val task = setUpFreeformTask()
+        val spyController = spy(controller)
+        val mockSurface = mock(SurfaceControl::class.java)
+        val mockDisplayLayout = mock(DisplayLayout::class.java)
+        whenever(displayController.getDisplayLayout(task.displayId)).thenReturn(mockDisplayLayout)
+        whenever(mockDisplayLayout.stableInsets()).thenReturn(Rect(0, 100, 2000, 2000))
+        spyController.onDragPositioningMove(task, mockSurface, 200f, Rect(100, -100, 500, 1000))
+
+        whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
+        whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull()))
+            .thenReturn(DesktopModeVisualIndicator.IndicatorType.NO_INDICATOR)
+        spyController.onDragPositioningEnd(
+            task,
+            mockSurface,
+            Point(100, -100), /* position */
+            PointF(200f, -200f), /* inputCoordinate */
+            Rect(100, -100, 500, 1000), /* currentDragBounds */
+            Rect(0, 50, 2000, 2000), /* validDragArea */
+            Rect() /* dragStartBounds */,
+            motionEvent,
+            desktopWindowDecoration,
+        )
+        val rectAfterEnd = Rect(100, 50, 500, 1150)
+        verify(transitions)
+            .startTransition(
+                eq(TRANSIT_CHANGE),
+                Mockito.argThat { wct ->
+                    return@argThat wct.changes.any { (token, change) ->
+                        change.configuration.windowConfiguration.bounds == rectAfterEnd
+                    }
+                },
+                eq(null),
+            )
+    }
+
+    @Test
+    fun onDesktopDragEnd_noIndicator_updatesTaskBounds() {
+        val task = setUpFreeformTask()
+        val spyController = spy(controller)
+        val mockSurface = mock(SurfaceControl::class.java)
+        val mockDisplayLayout = mock(DisplayLayout::class.java)
+        whenever(displayController.getDisplayLayout(task.displayId)).thenReturn(mockDisplayLayout)
+        whenever(mockDisplayLayout.stableInsets()).thenReturn(Rect(0, 100, 2000, 2000))
+        spyController.onDragPositioningMove(task, mockSurface, 200f, Rect(100, 200, 500, 1000))
+
+        val currentDragBounds = Rect(100, 200, 500, 1000)
+        whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
+        whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull()))
+            .thenReturn(DesktopModeVisualIndicator.IndicatorType.NO_INDICATOR)
+
+        spyController.onDragPositioningEnd(
+            task,
+            mockSurface,
+            Point(100, 200), /* position */
+            PointF(200f, 300f), /* inputCoordinate */
+            currentDragBounds, /* currentDragBounds */
+            Rect(0, 50, 2000, 2000) /* validDragArea */,
+            Rect() /* dragStartBounds */,
+            motionEvent,
+            desktopWindowDecoration,
+        )
+
+        verify(transitions)
+            .startTransition(
+                eq(TRANSIT_CHANGE),
+                Mockito.argThat { wct ->
+                    return@argThat wct.changes.any { (token, change) ->
+                        change.configuration.windowConfiguration.bounds == currentDragBounds
+                    }
+                },
+                eq(null),
+            )
+    }
+
+    @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
+        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,
+            Rect(0, 50, 2000, 2000) /* validDragArea */,
+            Rect() /* dragStartBounds */,
+            motionEvent,
+            desktopWindowDecoration,
+        )
+
+        // Assert bounds set to stable bounds
+        val wct = getLatestToggleResizeDesktopTaskWct(currentDragBounds)
+        assertThat(findBoundsChange(wct, task)).isEqualTo(STABLE_BOUNDS)
+        // Assert event is properly logged
+        verify(desktopModeEventLogger, times(1))
+            .logTaskResizingStarted(
+                ResizeTrigger.DRAG_TO_TOP_RESIZE_TRIGGER,
+                InputMethod.UNKNOWN_INPUT_METHOD,
+                task,
+                task.configuration.windowConfiguration.bounds.width(),
+                task.configuration.windowConfiguration.bounds.height(),
+                displayController,
+            )
+        verify(desktopModeEventLogger, times(1))
+            .logTaskResizingEnded(
+                ResizeTrigger.DRAG_TO_TOP_RESIZE_TRIGGER,
+                InputMethod.UNKNOWN_INPUT_METHOD,
+                task,
+                STABLE_BOUNDS.width(),
+                STABLE_BOUNDS.height(),
+                displayController,
+            )
+    }
+
+    @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(),
+            )
+        // Assert no event is logged
+        verify(desktopModeEventLogger, never())
+            .logTaskResizingStarted(any(), any(), any(), any(), any(), any(), any())
+        verify(desktopModeEventLogger, never())
+            .logTaskResizingEnded(any(), any(), any(), any(), any(), any(), any())
+    }
+
+    @Test
+    fun enterSplit_freeformTaskIsMovedToSplit() {
+        val task1 = setUpFreeformTask()
+        val task2 = setUpFreeformTask()
+        val task3 = setUpFreeformTask()
+
+        task1.isFocused = false
+        task2.isFocused = true
+        task3.isFocused = false
+
+        controller.enterSplit(DEFAULT_DISPLAY, leftOrTop = false)
+
+        verify(splitScreenController)
+            .requestEnterSplitSelect(
+                eq(task2),
+                any(),
+                eq(SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT),
+                eq(task2.configuration.windowConfiguration.bounds),
+            )
+    }
+
+    @Test
+    fun enterSplit_onlyVisibleNonMinimizedTask_removesWallpaperActivity() {
+        val task1 = setUpFreeformTask()
+        val task2 = setUpFreeformTask()
+        val task3 = setUpFreeformTask()
+        val wallpaperToken = MockToken().token()
+
+        task1.isFocused = false
+        task2.isFocused = true
+        task3.isFocused = false
+        taskRepository.wallpaperActivityToken = wallpaperToken
+        taskRepository.minimizeTask(DEFAULT_DISPLAY, task1.taskId)
+        taskRepository.updateTask(DEFAULT_DISPLAY, task3.taskId, isVisible = false)
+
+        controller.enterSplit(DEFAULT_DISPLAY, leftOrTop = false)
+
+        val wctArgument = ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
+        verify(splitScreenController)
+            .requestEnterSplitSelect(
+                eq(task2),
+                wctArgument.capture(),
+                eq(SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT),
+                eq(task2.configuration.windowConfiguration.bounds),
+            )
+        // Removes wallpaper activity when leaving desktop
+        wctArgument.value.assertRemoveAt(index = 0, wallpaperToken)
+    }
+
+    @Test
+    fun enterSplit_multipleVisibleNonMinimizedTasks_removesWallpaperActivity() {
+        val task1 = setUpFreeformTask()
+        val task2 = setUpFreeformTask()
+        val task3 = setUpFreeformTask()
+        val wallpaperToken = MockToken().token()
+
+        task1.isFocused = false
+        task2.isFocused = true
+        task3.isFocused = false
+        taskRepository.wallpaperActivityToken = wallpaperToken
+
+        controller.enterSplit(DEFAULT_DISPLAY, leftOrTop = false)
+
+        val wctArgument = ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
+        verify(splitScreenController)
+            .requestEnterSplitSelect(
+                eq(task2),
+                wctArgument.capture(),
+                eq(SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT),
+                eq(task2.configuration.windowConfiguration.bounds),
+            )
+        // Does not remove wallpaper activity, as desktop still has visible desktop tasks
+        assertThat(wctArgument.value.hierarchyOps).isEmpty()
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MULTI_INSTANCE_FEATURES)
+    fun newWindow_fromFullscreenOpensInSplit() {
+        setUpLandscapeDisplay()
+        val task = setUpFullscreenTask()
+        val optionsCaptor = ArgumentCaptor.forClass(Bundle::class.java)
+        runOpenNewWindow(task)
+        verify(splitScreenController)
+            .startIntent(
+                any(),
+                anyInt(),
+                any(),
+                any(),
+                optionsCaptor.capture(),
+                anyOrNull(),
+                eq(true),
+                eq(SPLIT_INDEX_UNDEFINED),
+            )
+        assertThat(ActivityOptions.fromBundle(optionsCaptor.value).launchWindowingMode)
+            .isEqualTo(WINDOWING_MODE_MULTI_WINDOW)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MULTI_INSTANCE_FEATURES)
+    fun newWindow_fromSplitOpensInSplit() {
+        setUpLandscapeDisplay()
+        val task = setUpSplitScreenTask()
+        val optionsCaptor = ArgumentCaptor.forClass(Bundle::class.java)
+        runOpenNewWindow(task)
+        verify(splitScreenController)
+            .startIntent(
+                any(),
+                anyInt(),
+                any(),
+                any(),
+                optionsCaptor.capture(),
+                anyOrNull(),
+                eq(true),
+                eq(SPLIT_INDEX_UNDEFINED),
+            )
+        assertThat(ActivityOptions.fromBundle(optionsCaptor.value).launchWindowingMode)
+            .isEqualTo(WINDOWING_MODE_MULTI_WINDOW)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MULTI_INSTANCE_FEATURES)
+    fun newWindow_fromFreeformAddsNewWindow() {
+        setUpLandscapeDisplay()
+        val task = setUpFreeformTask()
+        val wctCaptor = argumentCaptor<WindowContainerTransaction>()
+        val transition = Binder()
+        whenever(
+                mMockDesktopImmersiveController.exitImmersiveIfApplicable(
+                    any(),
+                    anyInt(),
+                    anyOrNull(),
+                    any(),
+                )
+            )
+            .thenReturn(ExitResult.NoExit)
+        whenever(
+                desktopMixedTransitionHandler.startLaunchTransition(
+                    anyInt(),
+                    any(),
+                    anyOrNull(),
+                    anyOrNull(),
+                    anyOrNull(),
+                )
+            )
+            .thenReturn(transition)
+
+        runOpenNewWindow(task)
+
+        verify(desktopMixedTransitionHandler)
+            .startLaunchTransition(
+                anyInt(),
+                wctCaptor.capture(),
+                anyOrNull(),
+                anyOrNull(),
+                anyOrNull(),
+            )
+        assertThat(
+                ActivityOptions.fromBundle(wctCaptor.firstValue.hierarchyOps[0].launchOptions)
+                    .launchWindowingMode
+            )
+            .isEqualTo(WINDOWING_MODE_FREEFORM)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MULTI_INSTANCE_FEATURES)
+    fun newWindow_fromFreeform_exitsImmersiveIfNeeded() {
+        setUpLandscapeDisplay()
+        val immersiveTask = setUpFreeformTask()
+        val task = setUpFreeformTask()
+        val runOnStart = RunOnStartTransitionCallback()
+        val transition = Binder()
+        whenever(
+                mMockDesktopImmersiveController.exitImmersiveIfApplicable(
+                    any(),
+                    anyInt(),
+                    anyOrNull(),
+                    any(),
+                )
+            )
+            .thenReturn(ExitResult.Exit(immersiveTask.taskId, runOnStart))
+        whenever(
+                desktopMixedTransitionHandler.startLaunchTransition(
+                    anyInt(),
+                    any(),
+                    anyOrNull(),
+                    anyOrNull(),
+                    anyOrNull(),
+                )
+            )
+            .thenReturn(transition)
+
+        runOpenNewWindow(task)
+
+        runOnStart.assertOnlyInvocation(transition)
+    }
+
+    private fun runOpenNewWindow(task: RunningTaskInfo) {
+        markTaskVisible(task)
+        task.baseActivity = mock(ComponentName::class.java)
+        task.isFocused = true
+        runningTasks.add(task)
+        controller.openNewWindow(task)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MULTI_INSTANCE_FEATURES)
+    fun openInstance_fromFullscreenOpensInSplit() {
+        setUpLandscapeDisplay()
+        val task = setUpFullscreenTask()
+        val taskToRequest = setUpFreeformTask()
+        val optionsCaptor = ArgumentCaptor.forClass(Bundle::class.java)
+        runOpenInstance(task, taskToRequest.taskId)
+        verify(splitScreenController)
+            .startTask(anyInt(), anyInt(), optionsCaptor.capture(), anyOrNull())
+        assertThat(ActivityOptions.fromBundle(optionsCaptor.value).launchWindowingMode)
+            .isEqualTo(WINDOWING_MODE_MULTI_WINDOW)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MULTI_INSTANCE_FEATURES)
+    fun openInstance_fromSplitOpensInSplit() {
+        setUpLandscapeDisplay()
+        val task = setUpSplitScreenTask()
+        val taskToRequest = setUpFreeformTask()
+        val optionsCaptor = ArgumentCaptor.forClass(Bundle::class.java)
+        runOpenInstance(task, taskToRequest.taskId)
+        verify(splitScreenController)
+            .startTask(anyInt(), anyInt(), optionsCaptor.capture(), anyOrNull())
+        assertThat(ActivityOptions.fromBundle(optionsCaptor.value).launchWindowingMode)
+            .isEqualTo(WINDOWING_MODE_MULTI_WINDOW)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MULTI_INSTANCE_FEATURES)
+    fun openInstance_fromFreeformAddsNewWindow() {
+        setUpLandscapeDisplay()
+        val task = setUpFreeformTask()
+        val taskToRequest = setUpFreeformTask()
+        runOpenInstance(task, taskToRequest.taskId)
+        verify(desktopMixedTransitionHandler)
+            .startLaunchTransition(anyInt(), any(), anyInt(), anyOrNull(), anyOrNull())
+        val wct = getLatestDesktopMixedTaskWct(type = TRANSIT_TO_FRONT)
+        assertThat(wct.hierarchyOps).hasSize(1)
+        wct.assertReorderAt(index = 0, taskToRequest)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MULTI_INSTANCE_FEATURES)
+    fun openInstance_fromFreeform_minimizesIfNeeded() {
+        setUpLandscapeDisplay()
+        val freeformTasks = (1..MAX_TASK_LIMIT + 1).map { _ -> setUpFreeformTask() }
+        val oldestTask = freeformTasks.first()
+        val newestTask = freeformTasks.last()
+
+        val transition = Binder()
+        val wctCaptor = argumentCaptor<WindowContainerTransaction>()
+        whenever(
+                desktopMixedTransitionHandler.startLaunchTransition(
+                    anyInt(),
+                    wctCaptor.capture(),
+                    anyInt(),
+                    anyOrNull(),
+                    anyOrNull(),
+                )
+            )
+            .thenReturn(transition)
+
+        runOpenInstance(newestTask, freeformTasks[1].taskId)
+
+        val wct = wctCaptor.firstValue
+        assertThat(wct.hierarchyOps.size).isEqualTo(2) // move-to-front + minimize
+        wct.assertReorderAt(0, freeformTasks[1], toTop = true)
+        wct.assertReorderAt(1, oldestTask, toTop = false)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MULTI_INSTANCE_FEATURES)
+    fun openInstance_fromFreeform_exitsImmersiveIfNeeded() {
+        setUpLandscapeDisplay()
+        val freeformTask = setUpFreeformTask()
+        val immersiveTask = setUpFreeformTask()
+        taskRepository.setTaskInFullImmersiveState(
+            displayId = immersiveTask.displayId,
+            taskId = immersiveTask.taskId,
+            immersive = true,
+        )
+        val runOnStartTransit = RunOnStartTransitionCallback()
+        val transition = Binder()
+        whenever(
+                desktopMixedTransitionHandler.startLaunchTransition(
+                    anyInt(),
+                    any(),
+                    anyInt(),
+                    anyOrNull(),
+                    anyOrNull(),
+                )
+            )
+            .thenReturn(transition)
+        whenever(
+                mMockDesktopImmersiveController.exitImmersiveIfApplicable(
+                    any(),
+                    eq(DEFAULT_DISPLAY),
+                    eq(freeformTask.taskId),
+                    any(),
+                )
+            )
+            .thenReturn(
+                ExitResult.Exit(
+                    exitingTask = immersiveTask.taskId,
+                    runOnTransitionStart = runOnStartTransit,
+                )
+            )
+
+        runOpenInstance(immersiveTask, freeformTask.taskId)
+
+        verify(mMockDesktopImmersiveController)
+            .exitImmersiveIfApplicable(
+                any(),
+                eq(immersiveTask.displayId),
+                eq(freeformTask.taskId),
+                any(),
+            )
+        runOnStartTransit.assertOnlyInvocation(transition)
+    }
+
+    private fun runOpenInstance(callingTask: RunningTaskInfo, requestedTaskId: Int) {
+        markTaskVisible(callingTask)
+        callingTask.baseActivity = mock(ComponentName::class.java)
+        callingTask.isFocused = true
+        runningTasks.add(callingTask)
+        controller.openInstance(callingTask, requestedTaskId)
+    }
+
+    @Test
+    fun toggleBounds_togglesToStableBounds() {
+        val bounds = Rect(0, 0, 100, 100)
+        val task = setUpFreeformTask(DEFAULT_DISPLAY, bounds)
+
+        controller.toggleDesktopTaskSize(
+            task,
+            ToggleTaskSizeInteraction(
+                ToggleTaskSizeInteraction.Direction.MAXIMIZE,
+                ToggleTaskSizeInteraction.Source.HEADER_BUTTON_TO_MAXIMIZE,
+                InputMethod.TOUCH,
+            ),
+        )
+
+        // Assert bounds set to stable bounds
+        val wct = getLatestToggleResizeDesktopTaskWct()
+        assertThat(findBoundsChange(wct, task)).isEqualTo(STABLE_BOUNDS)
+        verify(desktopModeEventLogger, times(1))
+            .logTaskResizingEnded(
+                ResizeTrigger.MAXIMIZE_BUTTON,
+                InputMethod.TOUCH,
+                task,
+                STABLE_BOUNDS.width(),
+                STABLE_BOUNDS.height(),
+                displayController,
+            )
+    }
+
+    @Test
+    @DisableFlags(Flags.FLAG_ENABLE_TILE_RESIZING)
+    fun snapToHalfScreen_getSnapBounds_calculatesBoundsForResizable() {
+        val bounds = Rect(100, 100, 300, 300)
+        val task =
+            setUpFreeformTask(DEFAULT_DISPLAY, bounds).apply {
+                topActivityInfo =
+                    ActivityInfo().apply {
+                        screenOrientation = SCREEN_ORIENTATION_LANDSCAPE
+                        configuration.windowConfiguration.appBounds = bounds
+                    }
+                isResizeable = true
+            }
+
+        val currentDragBounds = Rect(0, 100, 200, 300)
+        val expectedBounds =
+            Rect(
+                STABLE_BOUNDS.left,
+                STABLE_BOUNDS.top,
+                STABLE_BOUNDS.right / 2,
+                STABLE_BOUNDS.bottom,
+            )
+
+        controller.snapToHalfScreen(
+            task,
+            mockSurface,
+            currentDragBounds,
+            SnapPosition.LEFT,
+            ResizeTrigger.SNAP_LEFT_MENU,
+            InputMethod.TOUCH,
+            desktopWindowDecoration,
+        )
+        // Assert bounds set to stable bounds
+        val wct = getLatestToggleResizeDesktopTaskWct(currentDragBounds)
+        assertThat(findBoundsChange(wct, task)).isEqualTo(expectedBounds)
+        verify(desktopModeEventLogger, times(1))
+            .logTaskResizingEnded(
+                ResizeTrigger.SNAP_LEFT_MENU,
+                InputMethod.TOUCH,
+                task,
+                expectedBounds.width(),
+                expectedBounds.height(),
+                displayController,
+            )
+    }
+
+    @Test
+    @DisableFlags(Flags.FLAG_ENABLE_TILE_RESIZING)
+    fun snapToHalfScreen_snapBoundsWhenAlreadySnapped_animatesSurfaceWithoutWCT() {
+        // Set up task to already be in snapped-left bounds
+        val bounds =
+            Rect(
+                STABLE_BOUNDS.left,
+                STABLE_BOUNDS.top,
+                STABLE_BOUNDS.right / 2,
+                STABLE_BOUNDS.bottom,
+            )
+        val task =
+            setUpFreeformTask(DEFAULT_DISPLAY, bounds).apply {
+                topActivityInfo =
+                    ActivityInfo().apply {
+                        screenOrientation = SCREEN_ORIENTATION_LANDSCAPE
+                        configuration.windowConfiguration.appBounds = bounds
+                    }
+                isResizeable = true
+            }
+
+        // Attempt to snap left again
+        val currentDragBounds = Rect(bounds).apply { offset(-100, 0) }
+        controller.snapToHalfScreen(
+            task,
+            mockSurface,
+            currentDragBounds,
+            SnapPosition.LEFT,
+            ResizeTrigger.SNAP_LEFT_MENU,
+            InputMethod.TOUCH,
+            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(bounds), anyOrNull())
+        verify(desktopModeEventLogger, times(1))
+            .logTaskResizingEnded(
+                ResizeTrigger.SNAP_LEFT_MENU,
+                InputMethod.TOUCH,
+                task,
+                bounds.width(),
+                bounds.height(),
+                displayController,
+            )
+    }
+
+    @Test
+    @DisableFlags(
+        Flags.FLAG_DISABLE_NON_RESIZABLE_APP_SNAP_RESIZING,
+        Flags.FLAG_ENABLE_TILE_RESIZING,
+    )
+    fun handleSnapResizingTaskOnDrag_nonResizable_snapsToHalfScreen() {
+        val task =
+            setUpFreeformTask(DEFAULT_DISPLAY, Rect(0, 0, 200, 100)).apply { isResizeable = false }
+        val preDragBounds = Rect(100, 100, 400, 500)
+        val currentDragBounds = Rect(0, 100, 300, 500)
+        val expectedBounds =
+            Rect(
+                STABLE_BOUNDS.left,
+                STABLE_BOUNDS.top,
+                STABLE_BOUNDS.right / 2,
+                STABLE_BOUNDS.bottom,
+            )
+
+        controller.handleSnapResizingTaskOnDrag(
+            task,
+            SnapPosition.LEFT,
+            mockSurface,
+            currentDragBounds,
+            preDragBounds,
+            motionEvent,
+            desktopWindowDecoration,
+        )
+        val wct = getLatestToggleResizeDesktopTaskWct(currentDragBounds)
+        assertThat(findBoundsChange(wct, task)).isEqualTo(expectedBounds)
+        verify(desktopModeEventLogger, times(1))
+            .logTaskResizingStarted(
+                ResizeTrigger.DRAG_LEFT,
+                InputMethod.UNKNOWN_INPUT_METHOD,
+                task,
+                preDragBounds.width(),
+                preDragBounds.height(),
+                displayController,
+            )
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_DISABLE_NON_RESIZABLE_APP_SNAP_RESIZING)
+    fun handleSnapResizingTaskOnDrag_nonResizable_startsRepositionAnimation() {
+        val task =
+            setUpFreeformTask(DEFAULT_DISPLAY, Rect(0, 0, 200, 100)).apply { isResizeable = false }
+        val preDragBounds = Rect(100, 100, 400, 500)
+        val currentDragBounds = Rect(0, 100, 300, 500)
+
+        controller.handleSnapResizingTaskOnDrag(
+            task,
+            SnapPosition.LEFT,
+            mockSurface,
+            currentDragBounds,
+            preDragBounds,
+            motionEvent,
+            desktopWindowDecoration,
+        )
+        verify(mReturnToDragStartAnimator)
+            .start(
+                eq(task.taskId),
+                eq(mockSurface),
+                eq(currentDragBounds),
+                eq(preDragBounds),
+                any(),
+            )
+        verify(desktopModeEventLogger, never())
+            .logTaskResizingStarted(any(), any(), any(), any(), any(), any(), any())
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_DISABLE_NON_RESIZABLE_APP_SNAP_RESIZING)
+    fun handleInstantSnapResizingTask_nonResizable_animatorNotStartedAndShowsToast() {
+        val taskBounds = Rect(0, 0, 200, 100)
+        val task = setUpFreeformTask(DEFAULT_DISPLAY, taskBounds).apply { isResizeable = false }
+
+        controller.handleInstantSnapResizingTask(
+            task,
+            SnapPosition.LEFT,
+            ResizeTrigger.SNAP_LEFT_MENU,
+            InputMethod.MOUSE,
+            desktopWindowDecoration,
+        )
+
+        // Assert that task is NOT updated via WCT
+        verify(toggleResizeDesktopTaskTransitionHandler, never()).startTransition(any(), any())
+        verify(mockToast).show()
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_DISABLE_NON_RESIZABLE_APP_SNAP_RESIZING)
+    @DisableFlags(Flags.FLAG_ENABLE_TILE_RESIZING)
+    fun handleInstantSnapResizingTask_resizable_snapsToHalfScreenAndNotShowToast() {
+        val taskBounds = Rect(0, 0, 200, 100)
+        val task = setUpFreeformTask(DEFAULT_DISPLAY, taskBounds).apply { isResizeable = true }
+        val expectedBounds =
+            Rect(
+                STABLE_BOUNDS.left,
+                STABLE_BOUNDS.top,
+                STABLE_BOUNDS.right / 2,
+                STABLE_BOUNDS.bottom,
+            )
+
+        controller.handleInstantSnapResizingTask(
+            task,
+            SnapPosition.LEFT,
+            ResizeTrigger.SNAP_LEFT_MENU,
+            InputMethod.MOUSE,
+            desktopWindowDecoration,
+        )
+
+        // Assert bounds set to half of the stable bounds
+        val wct = getLatestToggleResizeDesktopTaskWct(taskBounds)
+        assertThat(findBoundsChange(wct, task)).isEqualTo(expectedBounds)
+        verify(mockToast, never()).show()
+        verify(desktopModeEventLogger, times(1))
+            .logTaskResizingStarted(
+                ResizeTrigger.SNAP_LEFT_MENU,
+                InputMethod.MOUSE,
+                task,
+                taskBounds.width(),
+                taskBounds.height(),
+                displayController,
+            )
+        verify(desktopModeEventLogger, times(1))
+            .logTaskResizingEnded(
+                ResizeTrigger.SNAP_LEFT_MENU,
+                InputMethod.MOUSE,
+                task,
+                expectedBounds.width(),
+                expectedBounds.height(),
+                displayController,
+            )
+    }
+
+    @Test
+    fun toggleBounds_togglesToCalculatedBoundsForNonResizable() {
+        val bounds = Rect(0, 0, 200, 100)
+        val task =
+            setUpFreeformTask(DEFAULT_DISPLAY, bounds).apply {
+                topActivityInfo =
+                    ActivityInfo().apply {
+                        screenOrientation = SCREEN_ORIENTATION_LANDSCAPE
+                        configuration.windowConfiguration.appBounds = bounds
+                    }
+                appCompatTaskInfo.topActivityLetterboxAppWidth = bounds.width()
+                appCompatTaskInfo.topActivityLetterboxAppHeight = bounds.height()
+                isResizeable = false
+            }
+
+        // Bounds should be 1000 x 500, vertically centered in the 1000 x 1000 stable bounds
+        val expectedBounds = Rect(STABLE_BOUNDS.left, 250, STABLE_BOUNDS.right, 750)
+
+        controller.toggleDesktopTaskSize(
+            task,
+            ToggleTaskSizeInteraction(
+                ToggleTaskSizeInteraction.Direction.MAXIMIZE,
+                ToggleTaskSizeInteraction.Source.HEADER_BUTTON_TO_MAXIMIZE,
+                InputMethod.TOUCH,
+            ),
+        )
+
+        // Assert bounds set to stable bounds
+        val wct = getLatestToggleResizeDesktopTaskWct()
+        assertThat(findBoundsChange(wct, task)).isEqualTo(expectedBounds)
+        verify(desktopModeEventLogger, times(1))
+            .logTaskResizingEnded(
+                ResizeTrigger.MAXIMIZE_BUTTON,
+                InputMethod.TOUCH,
+                task,
+                expectedBounds.width(),
+                expectedBounds.height(),
+                displayController,
+            )
+    }
+
+    @Test
+    fun toggleBounds_lastBoundsBeforeMaximizeSaved() {
+        val bounds = Rect(0, 0, 100, 100)
+        val task = setUpFreeformTask(DEFAULT_DISPLAY, bounds)
+
+        controller.toggleDesktopTaskSize(
+            task,
+            ToggleTaskSizeInteraction(
+                ToggleTaskSizeInteraction.Direction.MAXIMIZE,
+                ToggleTaskSizeInteraction.Source.HEADER_BUTTON_TO_MAXIMIZE,
+                InputMethod.TOUCH,
+            ),
+        )
+
+        assertThat(taskRepository.removeBoundsBeforeMaximize(task.taskId)).isEqualTo(bounds)
+        verify(desktopModeEventLogger, never())
+            .logTaskResizingEnded(any(), any(), any(), any(), any(), any(), any())
+    }
+
+    @Test
+    fun toggleBounds_togglesFromStableBoundsToLastBoundsBeforeMaximize() {
+        val boundsBeforeMaximize = Rect(0, 0, 100, 100)
+        val task = setUpFreeformTask(DEFAULT_DISPLAY, boundsBeforeMaximize)
+
+        // Maximize
+        controller.toggleDesktopTaskSize(
+            task,
+            ToggleTaskSizeInteraction(
+                ToggleTaskSizeInteraction.Direction.MAXIMIZE,
+                ToggleTaskSizeInteraction.Source.HEADER_BUTTON_TO_MAXIMIZE,
+                InputMethod.TOUCH,
+            ),
+        )
+        task.configuration.windowConfiguration.bounds.set(STABLE_BOUNDS)
+
+        // Restore
+        controller.toggleDesktopTaskSize(
+            task,
+            ToggleTaskSizeInteraction(
+                ToggleTaskSizeInteraction.Direction.RESTORE,
+                ToggleTaskSizeInteraction.Source.HEADER_BUTTON_TO_RESTORE,
+                InputMethod.TOUCH,
+            ),
+        )
+
+        // Assert bounds set to last bounds before maximize
+        val wct = getLatestToggleResizeDesktopTaskWct()
+        assertThat(findBoundsChange(wct, task)).isEqualTo(boundsBeforeMaximize)
+        verify(desktopModeEventLogger, times(1))
+            .logTaskResizingEnded(
+                ResizeTrigger.MAXIMIZE_BUTTON,
+                InputMethod.TOUCH,
+                task,
+                boundsBeforeMaximize.width(),
+                boundsBeforeMaximize.height(),
+                displayController,
+            )
+    }
+
+    @Test
+    fun toggleBounds_togglesFromStableBoundsToLastBoundsBeforeMaximize_nonResizeableEqualWidth() {
+        val boundsBeforeMaximize = Rect(0, 0, 100, 100)
+        val task =
+            setUpFreeformTask(DEFAULT_DISPLAY, boundsBeforeMaximize).apply { isResizeable = false }
+
+        // Maximize
+        controller.toggleDesktopTaskSize(
+            task,
+            ToggleTaskSizeInteraction(
+                ToggleTaskSizeInteraction.Direction.MAXIMIZE,
+                ToggleTaskSizeInteraction.Source.HEADER_BUTTON_TO_MAXIMIZE,
+                InputMethod.TOUCH,
+            ),
+        )
+        task.configuration.windowConfiguration.bounds.set(
+            STABLE_BOUNDS.left,
+            boundsBeforeMaximize.top,
+            STABLE_BOUNDS.right,
+            boundsBeforeMaximize.bottom,
+        )
+
+        // Restore
+        controller.toggleDesktopTaskSize(
+            task,
+            ToggleTaskSizeInteraction(
+                ToggleTaskSizeInteraction.Direction.RESTORE,
+                ToggleTaskSizeInteraction.Source.HEADER_BUTTON_TO_RESTORE,
+                InputMethod.TOUCH,
+            ),
+        )
+
+        // Assert bounds set to last bounds before maximize
+        val wct = getLatestToggleResizeDesktopTaskWct()
+        assertThat(findBoundsChange(wct, task)).isEqualTo(boundsBeforeMaximize)
+        verify(desktopModeEventLogger, times(1))
+            .logTaskResizingEnded(
+                ResizeTrigger.MAXIMIZE_BUTTON,
+                InputMethod.TOUCH,
+                task,
+                boundsBeforeMaximize.width(),
+                boundsBeforeMaximize.height(),
+                displayController,
+            )
+    }
+
+    @Test
+    fun toggleBounds_togglesFromStableBoundsToLastBoundsBeforeMaximize_nonResizeableEqualHeight() {
+        val boundsBeforeMaximize = Rect(0, 0, 100, 100)
+        val task =
+            setUpFreeformTask(DEFAULT_DISPLAY, boundsBeforeMaximize).apply { isResizeable = false }
+
+        // Maximize
+        controller.toggleDesktopTaskSize(
+            task,
+            ToggleTaskSizeInteraction(
+                ToggleTaskSizeInteraction.Direction.MAXIMIZE,
+                ToggleTaskSizeInteraction.Source.HEADER_BUTTON_TO_MAXIMIZE,
+                InputMethod.TOUCH,
+            ),
+        )
+        task.configuration.windowConfiguration.bounds.set(
+            boundsBeforeMaximize.left,
+            STABLE_BOUNDS.top,
+            boundsBeforeMaximize.right,
+            STABLE_BOUNDS.bottom,
+        )
+
+        // Restore
+        controller.toggleDesktopTaskSize(
+            task,
+            ToggleTaskSizeInteraction(
+                ToggleTaskSizeInteraction.Direction.RESTORE,
+                ToggleTaskSizeInteraction.Source.HEADER_BUTTON_TO_RESTORE,
+                InputMethod.TOUCH,
+            ),
+        )
+
+        // Assert bounds set to last bounds before maximize
+        val wct = getLatestToggleResizeDesktopTaskWct()
+        assertThat(findBoundsChange(wct, task)).isEqualTo(boundsBeforeMaximize)
+        verify(desktopModeEventLogger, times(1))
+            .logTaskResizingEnded(
+                ResizeTrigger.MAXIMIZE_BUTTON,
+                InputMethod.TOUCH,
+                task,
+                boundsBeforeMaximize.width(),
+                boundsBeforeMaximize.height(),
+                displayController,
+            )
+    }
+
+    @Test
+    fun toggleBounds_removesLastBoundsBeforeMaximizeAfterRestoringBounds() {
+        val boundsBeforeMaximize = Rect(0, 0, 100, 100)
+        val task = setUpFreeformTask(DEFAULT_DISPLAY, boundsBeforeMaximize)
+
+        // Maximize
+        controller.toggleDesktopTaskSize(
+            task,
+            ToggleTaskSizeInteraction(
+                ToggleTaskSizeInteraction.Direction.MAXIMIZE,
+                ToggleTaskSizeInteraction.Source.HEADER_BUTTON_TO_MAXIMIZE,
+                InputMethod.TOUCH,
+            ),
+        )
+        task.configuration.windowConfiguration.bounds.set(STABLE_BOUNDS)
+
+        // Restore
+        controller.toggleDesktopTaskSize(
+            task,
+            ToggleTaskSizeInteraction(
+                ToggleTaskSizeInteraction.Direction.RESTORE,
+                ToggleTaskSizeInteraction.Source.HEADER_BUTTON_TO_RESTORE,
+                InputMethod.TOUCH,
+            ),
+        )
+
+        // Assert last bounds before maximize removed after use
+        assertThat(taskRepository.removeBoundsBeforeMaximize(task.taskId)).isNull()
+        verify(desktopModeEventLogger, times(1))
+            .logTaskResizingEnded(
+                ResizeTrigger.MAXIMIZE_BUTTON,
+                InputMethod.TOUCH,
+                task,
+                boundsBeforeMaximize.width(),
+                boundsBeforeMaximize.height(),
+                displayController,
+            )
+    }
+
+    @Test
+    fun onUnhandledDrag_newFreeformIntent() {
+        testOnUnhandledDrag(
+            DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR,
+            PointF(1200f, 700f),
+            Rect(240, 700, 2160, 1900),
+        )
+    }
+
+    @Test
+    fun onUnhandledDrag_newFreeformIntentSplitLeft() {
+        testOnUnhandledDrag(
+            DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_LEFT_INDICATOR,
+            PointF(50f, 700f),
+            Rect(0, 0, 500, 1000),
+        )
+    }
+
+    @Test
+    fun onUnhandledDrag_newFreeformIntentSplitRight() {
+        testOnUnhandledDrag(
+            DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_RIGHT_INDICATOR,
+            PointF(2500f, 700f),
+            Rect(500, 0, 1000, 1000),
+        )
+    }
+
+    @Test
+    fun onUnhandledDrag_newFullscreenIntent() {
+        testOnUnhandledDrag(
+            DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR,
+            PointF(1200f, 50f),
+            Rect(),
+        )
+    }
+
+    @Test
+    fun shellController_registersUserChangeListener() {
+        verify(shellController, times(2)).addUserChangeListener(any())
+    }
+
+    @Test
+    @EnableFlags(FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
+    fun onTaskInfoChanged_inImmersiveUnrequestsImmersive_exits() {
+        val task = setUpFreeformTask(DEFAULT_DISPLAY)
+        taskRepository.setTaskInFullImmersiveState(DEFAULT_DISPLAY, task.taskId, immersive = true)
+
+        task.requestedVisibleTypes = WindowInsets.Type.statusBars()
+        controller.onTaskInfoChanged(task)
+
+        verify(mMockDesktopImmersiveController).moveTaskToNonImmersive(eq(task), any())
+    }
+
+    @Test
+    @EnableFlags(FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
+    fun onTaskInfoChanged_notInImmersiveUnrequestsImmersive_noReExit() {
+        val task = setUpFreeformTask(DEFAULT_DISPLAY)
+        taskRepository.setTaskInFullImmersiveState(DEFAULT_DISPLAY, task.taskId, immersive = false)
+
+        task.requestedVisibleTypes = WindowInsets.Type.statusBars()
+        controller.onTaskInfoChanged(task)
+
+        verify(mMockDesktopImmersiveController, never()).moveTaskToNonImmersive(eq(task), any())
+    }
+
+    @Test
+    @EnableFlags(FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
+    fun onTaskInfoChanged_inImmersiveUnrequestsImmersive_inRecentsTransition_noExit() {
+        val task = setUpFreeformTask(DEFAULT_DISPLAY)
+        taskRepository.setTaskInFullImmersiveState(DEFAULT_DISPLAY, task.taskId, immersive = true)
+        recentsTransitionStateListener.onTransitionStateChanged(TRANSITION_STATE_REQUESTED)
+
+        task.requestedVisibleTypes = WindowInsets.Type.statusBars()
+        controller.onTaskInfoChanged(task)
+
+        verify(mMockDesktopImmersiveController, never()).moveTaskToNonImmersive(eq(task), any())
+    }
+
+    @Test
+    fun moveTaskToDesktop_background_attemptsImmersiveExit() {
+        val task = setUpFreeformTask(background = true)
+        val wct = WindowContainerTransaction()
+        val runOnStartTransit = RunOnStartTransitionCallback()
+        val transition = Binder()
+        whenever(
+                mMockDesktopImmersiveController.exitImmersiveIfApplicable(
+                    eq(wct),
+                    eq(task.displayId),
+                    eq(task.taskId),
+                    any(),
+                )
+            )
+            .thenReturn(ExitResult.Exit(exitingTask = 5, runOnTransitionStart = runOnStartTransit))
+        whenever(enterDesktopTransitionHandler.moveToDesktop(wct, UNKNOWN)).thenReturn(transition)
+
+        controller.moveTaskToDesktop(taskId = task.taskId, wct = wct, transitionSource = UNKNOWN)
+
+        verify(mMockDesktopImmersiveController)
+            .exitImmersiveIfApplicable(eq(wct), eq(task.displayId), eq(task.taskId), any())
+        runOnStartTransit.assertOnlyInvocation(transition)
+    }
+
+    @Test
+    fun moveTaskToDesktop_foreground_attemptsImmersiveExit() {
+        val task = setUpFreeformTask(background = false)
+        val wct = WindowContainerTransaction()
+        val runOnStartTransit = RunOnStartTransitionCallback()
+        val transition = Binder()
+        whenever(
+                mMockDesktopImmersiveController.exitImmersiveIfApplicable(
+                    eq(wct),
+                    eq(task.displayId),
+                    eq(task.taskId),
+                    any(),
+                )
+            )
+            .thenReturn(ExitResult.Exit(exitingTask = 5, runOnTransitionStart = runOnStartTransit))
+        whenever(enterDesktopTransitionHandler.moveToDesktop(wct, UNKNOWN)).thenReturn(transition)
+
+        controller.moveTaskToDesktop(taskId = task.taskId, wct = wct, transitionSource = UNKNOWN)
+
+        verify(mMockDesktopImmersiveController)
+            .exitImmersiveIfApplicable(eq(wct), eq(task.displayId), eq(task.taskId), any())
+        runOnStartTransit.assertOnlyInvocation(transition)
+    }
+
+    @Test
+    fun moveTaskToFront_background_attemptsImmersiveExit() {
+        val task = setUpFreeformTask(background = true)
+        val runOnStartTransit = RunOnStartTransitionCallback()
+        val transition = Binder()
+        whenever(
+                mMockDesktopImmersiveController.exitImmersiveIfApplicable(
+                    any(),
+                    eq(task.displayId),
+                    eq(task.taskId),
+                    any(),
+                )
+            )
+            .thenReturn(ExitResult.Exit(exitingTask = 5, runOnTransitionStart = runOnStartTransit))
+        whenever(
+                desktopMixedTransitionHandler.startLaunchTransition(
+                    any(),
+                    any(),
+                    anyInt(),
+                    anyOrNull(),
+                    anyOrNull(),
+                )
+            )
+            .thenReturn(transition)
+
+        controller.moveTaskToFront(task.taskId, remoteTransition = null)
+
+        verify(mMockDesktopImmersiveController)
+            .exitImmersiveIfApplicable(any(), eq(task.displayId), eq(task.taskId), any())
+        runOnStartTransit.assertOnlyInvocation(transition)
+    }
+
+    @Test
+    fun moveTaskToFront_foreground_attemptsImmersiveExit() {
+        val task = setUpFreeformTask(background = false)
+        val runOnStartTransit = RunOnStartTransitionCallback()
+        val transition = Binder()
+        whenever(
+                mMockDesktopImmersiveController.exitImmersiveIfApplicable(
+                    any(),
+                    eq(task.displayId),
+                    eq(task.taskId),
+                    any(),
+                )
+            )
+            .thenReturn(ExitResult.Exit(exitingTask = 5, runOnTransitionStart = runOnStartTransit))
+        whenever(
+                desktopMixedTransitionHandler.startLaunchTransition(
+                    any(),
+                    any(),
+                    eq(task.taskId),
+                    anyOrNull(),
+                    anyOrNull(),
+                )
+            )
+            .thenReturn(transition)
+
+        controller.moveTaskToFront(task.taskId, remoteTransition = null)
+
+        verify(mMockDesktopImmersiveController)
+            .exitImmersiveIfApplicable(any(), eq(task.displayId), eq(task.taskId), any())
+        runOnStartTransit.assertOnlyInvocation(transition)
+    }
+
+    @Test
+    fun handleRequest_freeformLaunchToDesktop_attemptsImmersiveExit() {
+        markTaskVisible(setUpFreeformTask())
+        val task = setUpFreeformTask()
+        markTaskVisible(task)
+        val binder = Binder()
+
+        controller.handleRequest(binder, createTransition(task))
+
+        verify(mMockDesktopImmersiveController)
+            .exitImmersiveIfApplicable(eq(binder), any(), eq(task.displayId), any())
+    }
+
+    @Test
+    fun handleRequest_fullscreenLaunchToDesktop_attemptsImmersiveExit() {
+        setUpFreeformTask()
+        val task = setUpFullscreenTask()
+        val binder = Binder()
+
+        controller.handleRequest(binder, createTransition(task))
+
+        verify(mMockDesktopImmersiveController)
+            .exitImmersiveIfApplicable(eq(binder), any(), eq(task.displayId), any())
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
+    fun shouldPlayDesktopAnimation_notShowingDesktop_doesNotPlay() {
+        val triggerTask = setUpFullscreenTask(displayId = 5)
+        taskRepository.setTaskInFullImmersiveState(
+            displayId = triggerTask.displayId,
+            taskId = triggerTask.taskId,
+            immersive = true,
+        )
+
+        assertThat(
+                controller.shouldPlayDesktopAnimation(
+                    TransitionRequestInfo(TRANSIT_OPEN, triggerTask, /* remoteTransition= */ null)
+                )
+            )
+            .isFalse()
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
+    fun shouldPlayDesktopAnimation_notOpening_doesNotPlay() {
+        val triggerTask = setUpFreeformTask(displayId = 5)
+        taskRepository.setTaskInFullImmersiveState(
+            displayId = triggerTask.displayId,
+            taskId = triggerTask.taskId,
+            immersive = true,
+        )
+
+        assertThat(
+                controller.shouldPlayDesktopAnimation(
+                    TransitionRequestInfo(TRANSIT_CHANGE, triggerTask, /* remoteTransition= */ null)
+                )
+            )
+            .isFalse()
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
+    fun shouldPlayDesktopAnimation_notImmersive_doesNotPlay() {
+        val triggerTask = setUpFreeformTask(displayId = 5)
+        taskRepository.setTaskInFullImmersiveState(
+            displayId = triggerTask.displayId,
+            taskId = triggerTask.taskId,
+            immersive = false,
+        )
+
+        assertThat(
+                controller.shouldPlayDesktopAnimation(
+                    TransitionRequestInfo(TRANSIT_OPEN, triggerTask, /* remoteTransition= */ null)
+                )
+            )
+            .isFalse()
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
+    fun shouldPlayDesktopAnimation_fullscreenEntersDesktop_plays() {
+        // At least one freeform task to be in a desktop.
+        val existingTask = setUpFreeformTask(displayId = 5)
+        val triggerTask = setUpFullscreenTask(displayId = 5)
+        assertThat(controller.isDesktopModeShowing(triggerTask.displayId)).isTrue()
+        taskRepository.setTaskInFullImmersiveState(
+            displayId = existingTask.displayId,
+            taskId = existingTask.taskId,
+            immersive = true,
+        )
+
+        assertThat(
+                controller.shouldPlayDesktopAnimation(
+                    TransitionRequestInfo(TRANSIT_OPEN, triggerTask, /* remoteTransition= */ null)
+                )
+            )
+            .isTrue()
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
+    fun shouldPlayDesktopAnimation_fullscreenStaysFullscreen_doesNotPlay() {
+        val triggerTask = setUpFullscreenTask(displayId = 5)
+        assertThat(controller.isDesktopModeShowing(triggerTask.displayId)).isFalse()
+
+        assertThat(
+                controller.shouldPlayDesktopAnimation(
+                    TransitionRequestInfo(TRANSIT_OPEN, triggerTask, /* remoteTransition= */ null)
+                )
+            )
+            .isFalse()
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
+    fun shouldPlayDesktopAnimation_freeformStaysInDesktop_plays() {
+        // At least one freeform task to be in a desktop.
+        val existingTask = setUpFreeformTask(displayId = 5)
+        val triggerTask = setUpFreeformTask(displayId = 5, active = false)
+        assertThat(controller.isDesktopModeShowing(triggerTask.displayId)).isTrue()
+        taskRepository.setTaskInFullImmersiveState(
+            displayId = existingTask.displayId,
+            taskId = existingTask.taskId,
+            immersive = true,
+        )
+
+        assertThat(
+                controller.shouldPlayDesktopAnimation(
+                    TransitionRequestInfo(TRANSIT_OPEN, triggerTask, /* remoteTransition= */ null)
+                )
+            )
+            .isTrue()
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
+    fun shouldPlayDesktopAnimation_freeformExitsDesktop_doesNotPlay() {
+        val triggerTask = setUpFreeformTask(displayId = 5, active = false)
+        assertThat(controller.isDesktopModeShowing(triggerTask.displayId)).isFalse()
+
+        assertThat(
+                controller.shouldPlayDesktopAnimation(
+                    TransitionRequestInfo(TRANSIT_OPEN, triggerTask, /* remoteTransition= */ null)
+                )
+            )
+            .isFalse()
+    }
+
+    private class RunOnStartTransitionCallback : ((IBinder) -> Unit) {
+        var invocations = 0
+            private set
+
+        var lastInvoked: IBinder? = null
+            private set
+
+        override fun invoke(transition: IBinder) {
+            invocations++
+            lastInvoked = transition
+        }
+    }
+
+    private fun RunOnStartTransitionCallback.assertOnlyInvocation(transition: IBinder) {
+        assertThat(invocations).isEqualTo(1)
+        assertThat(lastInvoked).isEqualTo(transition)
+    }
+
+    /**
+     * Assert that an unhandled drag event launches a PendingIntent with the windowing mode and
+     * bounds we are expecting.
+     */
+    private fun testOnUnhandledDrag(
+        indicatorType: DesktopModeVisualIndicator.IndicatorType,
+        inputCoordinate: PointF,
+        expectedBounds: Rect,
+    ) {
+        setUpLandscapeDisplay()
+        val task = setUpFreeformTask()
+        markTaskVisible(task)
+        task.isFocused = true
+        val runningTasks = ArrayList<RunningTaskInfo>()
+        runningTasks.add(task)
+        val spyController = spy(controller)
+        val mockPendingIntent = mock(PendingIntent::class.java)
+        val mockDragEvent = mock(DragEvent::class.java)
+        val mockCallback = mock(Consumer::class.java)
+        val b = SurfaceControl.Builder()
+        b.setName("test surface")
+        val dragSurface = b.build()
+        whenever(shellTaskOrganizer.runningTasks).thenReturn(runningTasks)
+        whenever(mockDragEvent.dragSurface).thenReturn(dragSurface)
+        whenever(mockDragEvent.x).thenReturn(inputCoordinate.x)
+        whenever(mockDragEvent.y).thenReturn(inputCoordinate.y)
+        whenever(multiInstanceHelper.supportsMultiInstanceSplit(anyOrNull())).thenReturn(true)
+        whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
+        doReturn(indicatorType)
+            .whenever(spyController)
+            .updateVisualIndicator(
+                eq(task),
+                anyOrNull(),
+                anyOrNull(),
+                anyOrNull(),
+                eq(DesktopModeVisualIndicator.DragStartState.DRAGGED_INTENT),
+            )
+
+        spyController.onUnhandledDrag(
+            mockPendingIntent,
+            mockDragEvent,
+            mockCallback as Consumer<Boolean>,
+        )
+        val arg: ArgumentCaptor<WindowContainerTransaction> =
+            ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
+        var expectedWindowingMode: Int
+        if (indicatorType == DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR) {
+            expectedWindowingMode = WINDOWING_MODE_FULLSCREEN
+            // Fullscreen launches currently use default transitions
+            verify(transitions).startTransition(any(), capture(arg), anyOrNull())
+        } else {
+            expectedWindowingMode = WINDOWING_MODE_FREEFORM
+            // All other launches use a special handler.
+            verify(dragAndDropTransitionHandler).handleDropEvent(capture(arg))
+        }
+        assertThat(
+                ActivityOptions.fromBundle(arg.value.hierarchyOps[0].launchOptions)
+                    .launchWindowingMode
+            )
+            .isEqualTo(expectedWindowingMode)
+        assertThat(ActivityOptions.fromBundle(arg.value.hierarchyOps[0].launchOptions).launchBounds)
+            .isEqualTo(expectedBounds)
+    }
+
+    private val desktopWallpaperIntent: Intent
+        get() = Intent(context, DesktopWallpaperActivity::class.java)
+
+    private fun addFreeformTaskAtPosition(
+        pos: DesktopTaskPosition,
+        stableBounds: Rect,
+        bounds: Rect = DEFAULT_LANDSCAPE_BOUNDS,
+        offsetPos: Point = Point(0, 0),
+    ): RunningTaskInfo {
+        val offset = pos.getTopLeftCoordinates(stableBounds, bounds)
+        val prevTaskBounds = Rect(bounds)
+        prevTaskBounds.offsetTo(offset.x + offsetPos.x, offset.y + offsetPos.y)
+        return setUpFreeformTask(bounds = prevTaskBounds)
+    }
+
+    private fun setUpFreeformTask(
+        displayId: Int = DEFAULT_DISPLAY,
+        bounds: Rect? = null,
+        active: Boolean = true,
+        background: Boolean = false,
+    ): RunningTaskInfo {
+        val task = createFreeformTask(displayId, bounds)
+        val activityInfo = ActivityInfo()
+        task.topActivityInfo = activityInfo
+        if (background) {
+            whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(null)
+            whenever(recentTasksController.findTaskInBackground(task.taskId))
+                .thenReturn(createTaskInfo(task.taskId))
+        } else {
+            whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
+        }
+        taskRepository.addTask(displayId, task.taskId, isVisible = active)
+        if (!background) {
+            runningTasks.add(task)
+        }
+        return task
+    }
+
+    private fun setUpPipTask(autoEnterEnabled: Boolean): RunningTaskInfo {
+        return setUpFreeformTask().apply {
+            pictureInPictureParams =
+                PictureInPictureParams.Builder().setAutoEnterEnabled(autoEnterEnabled).build()
+        }
+    }
+
+    private fun setUpHomeTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo {
+        val task = createHomeTask(displayId)
+        whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
+        runningTasks.add(task)
+        return task
+    }
+
+    private fun setUpFullscreenTask(
+        displayId: Int = DEFAULT_DISPLAY,
+        isResizable: Boolean = true,
+        windowingMode: Int = WINDOWING_MODE_FULLSCREEN,
+        deviceOrientation: Int = ORIENTATION_LANDSCAPE,
+        screenOrientation: Int = SCREEN_ORIENTATION_UNSPECIFIED,
+        shouldLetterbox: Boolean = false,
+        gravity: Int = Gravity.NO_GRAVITY,
+        enableUserFullscreenOverride: Boolean = false,
+        enableSystemFullscreenOverride: Boolean = false,
+        aspectRatioOverrideApplied: Boolean = false,
+    ): RunningTaskInfo {
+        val task = createFullscreenTask(displayId)
+        val activityInfo = ActivityInfo()
+        activityInfo.screenOrientation = screenOrientation
+        activityInfo.windowLayout = ActivityInfo.WindowLayout(0, 0F, 0, 0F, gravity, 0, 0)
+        with(task) {
+            topActivityInfo = activityInfo
+            isResizeable = isResizable
+            configuration.orientation = deviceOrientation
+            configuration.windowConfiguration.windowingMode = windowingMode
+            appCompatTaskInfo.isUserFullscreenOverrideEnabled = enableUserFullscreenOverride
+            appCompatTaskInfo.isSystemFullscreenOverrideEnabled = enableSystemFullscreenOverride
+
+            if (deviceOrientation == ORIENTATION_LANDSCAPE) {
+                configuration.windowConfiguration.appBounds =
+                    Rect(0, 0, DISPLAY_DIMENSION_LONG, DISPLAY_DIMENSION_SHORT)
+                appCompatTaskInfo.topActivityLetterboxAppWidth = DISPLAY_DIMENSION_LONG
+                appCompatTaskInfo.topActivityLetterboxAppHeight = DISPLAY_DIMENSION_SHORT
+            } else {
+                configuration.windowConfiguration.appBounds =
+                    Rect(0, 0, DISPLAY_DIMENSION_SHORT, DISPLAY_DIMENSION_LONG)
+                appCompatTaskInfo.topActivityLetterboxAppWidth = DISPLAY_DIMENSION_SHORT
+                appCompatTaskInfo.topActivityLetterboxAppHeight = DISPLAY_DIMENSION_LONG
+            }
+
+            if (shouldLetterbox) {
+                appCompatTaskInfo.setHasMinAspectRatioOverride(aspectRatioOverrideApplied)
+                if (
+                    deviceOrientation == ORIENTATION_LANDSCAPE &&
+                        screenOrientation == SCREEN_ORIENTATION_PORTRAIT
+                ) {
+                    // Letterbox to portrait size
+                    appCompatTaskInfo.setTopActivityLetterboxed(true)
+                    appCompatTaskInfo.topActivityLetterboxAppWidth = 1200
+                    appCompatTaskInfo.topActivityLetterboxAppHeight = 1600
+                } else if (
+                    deviceOrientation == ORIENTATION_PORTRAIT &&
+                        screenOrientation == SCREEN_ORIENTATION_LANDSCAPE
+                ) {
+                    // Letterbox to landscape size
+                    appCompatTaskInfo.setTopActivityLetterboxed(true)
+                    appCompatTaskInfo.topActivityLetterboxAppWidth = 1600
+                    appCompatTaskInfo.topActivityLetterboxAppHeight = 1200
+                }
+            }
+        }
+        whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
+        runningTasks.add(task)
+        return task
+    }
+
+    private fun setUpLandscapeDisplay() {
+        whenever(displayLayout.width()).thenReturn(DISPLAY_DIMENSION_LONG)
+        whenever(displayLayout.height()).thenReturn(DISPLAY_DIMENSION_SHORT)
+        val stableBounds =
+            Rect(
+                0,
+                0,
+                DISPLAY_DIMENSION_LONG,
+                DISPLAY_DIMENSION_SHORT - Companion.TASKBAR_FRAME_HEIGHT,
+            )
+        whenever(displayLayout.getStableBoundsForDesktopMode(any())).thenAnswer { i ->
+            (i.arguments.first() as Rect).set(stableBounds)
+        }
+    }
+
+    private fun setUpPortraitDisplay() {
+        whenever(displayLayout.width()).thenReturn(DISPLAY_DIMENSION_SHORT)
+        whenever(displayLayout.height()).thenReturn(DISPLAY_DIMENSION_LONG)
+        val stableBounds =
+            Rect(
+                0,
+                0,
+                DISPLAY_DIMENSION_SHORT,
+                DISPLAY_DIMENSION_LONG - Companion.TASKBAR_FRAME_HEIGHT,
+            )
+        whenever(displayLayout.getStableBoundsForDesktopMode(any())).thenAnswer { i ->
+            (i.arguments.first() as Rect).set(stableBounds)
+        }
+    }
+
+    private fun setUpSplitScreenTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo {
+        val task = createSplitScreenTask(displayId)
+        whenever(splitScreenController.isTaskInSplitScreen(task.taskId)).thenReturn(true)
+        whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
+        runningTasks.add(task)
+        return task
+    }
+
+    private fun markTaskVisible(task: RunningTaskInfo) {
+        taskRepository.updateTask(task.displayId, task.taskId, isVisible = true)
+    }
+
+    private fun markTaskHidden(task: RunningTaskInfo) {
+        taskRepository.updateTask(task.displayId, task.taskId, isVisible = false)
+    }
+
+    private fun getLatestWct(
+        @WindowManager.TransitionType type: Int = TRANSIT_OPEN,
+        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 {
+            verify(transitions).startTransition(eq(type), arg.capture(), isA(handlerClass))
+        }
+        return arg.value
+    }
+
+    private fun getLatestToggleResizeDesktopTaskWct(
+        currentBounds: Rect? = null
+    ): WindowContainerTransaction {
+        val arg: ArgumentCaptor<WindowContainerTransaction> =
+            ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
+        verify(toggleResizeDesktopTaskTransitionHandler, atLeastOnce())
+            .startTransition(capture(arg), eq(currentBounds))
+        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())
+        return arg.value
+    }
+
+    private fun getLatestDragToDesktopWct(): WindowContainerTransaction {
+        val arg: ArgumentCaptor<WindowContainerTransaction> =
+            ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
+        verify(dragToDesktopTransitionHandler).finishDragToDesktopTransition(capture(arg))
+        return arg.value
+    }
+
+    private fun getLatestExitDesktopWct(): WindowContainerTransaction {
+        val arg = ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
+        verify(exitDesktopTransitionHandler).startTransition(any(), arg.capture(), any(), any())
+        return arg.value
+    }
+
+    private fun findBoundsChange(wct: WindowContainerTransaction, task: RunningTaskInfo): Rect? =
+        wct.changes[task.token.asBinder()]?.configuration?.windowConfiguration?.bounds
+
+    private fun verifyWCTNotExecuted() {
+        verify(transitions, never()).startTransition(anyInt(), any(), isNull())
+    }
+
+    private fun verifyExitDesktopWCTNotExecuted() {
+        verify(exitDesktopTransitionHandler, never()).startTransition(any(), any(), any(), any())
+    }
+
+    private fun verifyEnterDesktopWCTNotExecuted() {
+        verify(enterDesktopTransitionHandler, never()).moveToDesktop(any(), any())
+    }
+
+    private fun createTransition(
+        task: RunningTaskInfo?,
+        @WindowManager.TransitionType type: Int = TRANSIT_OPEN,
+    ): TransitionRequestInfo {
+        return TransitionRequestInfo(type, task, null /* remoteTransition */)
+    }
+
+    private companion object {
+        const val SECOND_DISPLAY = 2
+        val STABLE_BOUNDS = Rect(0, 0, 1000, 1000)
+        const val MAX_TASK_LIMIT = 6
+        private const val TASKBAR_FRAME_HEIGHT = 200
+    }
 }
 
 private fun WindowContainerTransaction.assertIndexInBounds(index: Int) {
-  assertWithMessage("WCT does not have a hierarchy operation at index $index")
-      .that(hierarchyOps.size)
-      .isGreaterThan(index)
+    assertWithMessage("WCT does not have a hierarchy operation at index $index")
+        .that(hierarchyOps.size)
+        .isGreaterThan(index)
 }
 
 private fun WindowContainerTransaction.assertReorderAt(
     index: Int,
     task: RunningTaskInfo,
-    toTop: Boolean? = null
+    toTop: Boolean? = null,
 ) {
-  assertIndexInBounds(index)
-  val op = hierarchyOps[index]
-  assertThat(op.type).isEqualTo(HIERARCHY_OP_TYPE_REORDER)
-  assertThat(op.container).isEqualTo(task.token.asBinder())
-  toTop?.let { assertThat(op.toTop).isEqualTo(it) }
+    assertIndexInBounds(index)
+    val op = hierarchyOps[index]
+    assertThat(op.type).isEqualTo(HIERARCHY_OP_TYPE_REORDER)
+    assertThat(op.container).isEqualTo(task.token.asBinder())
+    toTop?.let { assertThat(op.toTop).isEqualTo(it) }
 }
 
 private fun WindowContainerTransaction.assertReorderSequence(vararg tasks: RunningTaskInfo) {
-  for (i in tasks.indices) {
-    assertReorderAt(i, tasks[i])
-  }
+    for (i in tasks.indices) {
+        assertReorderAt(i, tasks[i])
+    }
 }
 
 /** Checks if the reorder hierarchy operations in [range] correspond to [tasks] list */
 private fun WindowContainerTransaction.assertReorderSequenceInRange(
-  range: IntRange,
-  vararg tasks: RunningTaskInfo
+    range: IntRange,
+    vararg tasks: RunningTaskInfo,
 ) {
-  assertThat(hierarchyOps.slice(range).map { it.type to it.container })
-    .containsExactlyElementsIn(tasks.map { HIERARCHY_OP_TYPE_REORDER to it.token.asBinder() })
-    .inOrder()
+    assertThat(hierarchyOps.slice(range).map { it.type to it.container })
+        .containsExactlyElementsIn(tasks.map { HIERARCHY_OP_TYPE_REORDER to it.token.asBinder() })
+        .inOrder()
 }
 
 private fun WindowContainerTransaction.assertRemoveAt(index: Int, token: WindowContainerToken) {
-  assertIndexInBounds(index)
-  val op = hierarchyOps[index]
-  assertThat(op.type).isEqualTo(HIERARCHY_OP_TYPE_REMOVE_TASK)
-  assertThat(op.container).isEqualTo(token.asBinder())
+    assertIndexInBounds(index)
+    val op = hierarchyOps[index]
+    assertThat(op.type).isEqualTo(HIERARCHY_OP_TYPE_REMOVE_TASK)
+    assertThat(op.container).isEqualTo(token.asBinder())
 }
 
 private fun WindowContainerTransaction.assertNoRemoveAt(index: Int, token: WindowContainerToken) {
-  assertIndexInBounds(index)
-  val op = hierarchyOps[index]
-  assertThat(op.type).isEqualTo(HIERARCHY_OP_TYPE_REMOVE_TASK)
-  assertThat(op.container).isEqualTo(token.asBinder())
+    assertIndexInBounds(index)
+    val op = hierarchyOps[index]
+    assertThat(op.type).isEqualTo(HIERARCHY_OP_TYPE_REMOVE_TASK)
+    assertThat(op.container).isEqualTo(token.asBinder())
 }
 
 private fun WindowContainerTransaction.hasRemoveAt(index: Int, token: WindowContainerToken) {
-  assertIndexInBounds(index)
-  val op = hierarchyOps[index]
-  assertThat(op.type).isEqualTo(HIERARCHY_OP_TYPE_REMOVE_TASK)
-  assertThat(op.container).isEqualTo(token.asBinder())
+    assertIndexInBounds(index)
+    val op = hierarchyOps[index]
+    assertThat(op.type).isEqualTo(HIERARCHY_OP_TYPE_REMOVE_TASK)
+    assertThat(op.container).isEqualTo(token.asBinder())
 }
 
 private fun WindowContainerTransaction.assertPendingIntentAt(index: Int, intent: Intent) {
-  assertIndexInBounds(index)
-  val op = hierarchyOps[index]
-  assertThat(op.type).isEqualTo(HIERARCHY_OP_TYPE_PENDING_INTENT)
-  assertThat(op.pendingIntent?.intent?.component).isEqualTo(intent.component)
+    assertIndexInBounds(index)
+    val op = hierarchyOps[index]
+    assertThat(op.type).isEqualTo(HIERARCHY_OP_TYPE_PENDING_INTENT)
+    assertThat(op.pendingIntent?.intent?.component).isEqualTo(intent.component)
 }
 
 private fun WindowContainerTransaction.assertLaunchTaskAt(
     index: Int,
     taskId: Int,
-    windowingMode: Int
+    windowingMode: Int,
 ) {
-  val keyLaunchWindowingMode = "android.activity.windowingMode"
+    val keyLaunchWindowingMode = "android.activity.windowingMode"
 
-  assertIndexInBounds(index)
-  val op = hierarchyOps[index]
-  assertThat(op.type).isEqualTo(HIERARCHY_OP_TYPE_LAUNCH_TASK)
-  assertThat(op.launchOptions?.getInt(LAUNCH_KEY_TASK_ID)).isEqualTo(taskId)
-  assertThat(op.launchOptions?.getInt(keyLaunchWindowingMode, WINDOWING_MODE_UNDEFINED))
-      .isEqualTo(windowingMode)
+    assertIndexInBounds(index)
+    val op = hierarchyOps[index]
+    assertThat(op.type).isEqualTo(HIERARCHY_OP_TYPE_LAUNCH_TASK)
+    assertThat(op.launchOptions?.getInt(LAUNCH_KEY_TASK_ID)).isEqualTo(taskId)
+    assertThat(op.launchOptions?.getInt(keyLaunchWindowingMode, WINDOWING_MODE_UNDEFINED))
+        .isEqualTo(windowingMode)
 }
 
 private fun WindowContainerTransaction?.anyDensityConfigChange(
     token: WindowContainerToken
 ): Boolean {
-  return this?.changes?.any { change ->
-    change.key == token.asBinder() && ((change.value.configSetMask and CONFIG_DENSITY) != 0)
-  } ?: false
+    return this?.changes?.any { change ->
+        change.key == token.asBinder() && ((change.value.configSetMask and CONFIG_DENSITY) != 0)
+    } ?: false
 }
 
 private fun WindowContainerTransaction?.anyWindowingModeChange(
-  token: WindowContainerToken
+    token: WindowContainerToken
 ): Boolean {
-return this?.changes?.any { change ->
-  change.key == token.asBinder() && change.value.windowingMode >= 0
-} ?: false
+    return this?.changes?.any { change ->
+        change.key == token.asBinder() && change.value.windowingMode >= 0
+    } ?: false
 }
 
 private fun createTaskInfo(id: Int) =
     RecentTaskInfo().apply {
-      taskId = id
-      token = WindowContainerToken(mock(IWindowContainerToken::class.java))
+        taskId = id
+        token = WindowContainerToken(mock(IWindowContainerToken::class.java))
     }
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 0712d58..e6f1fcf 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
@@ -47,6 +47,7 @@
 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.ShellController
 import com.android.wm.shell.sysui.ShellInit
 import com.android.wm.shell.transition.TransitionInfoBuilder
 import com.android.wm.shell.transition.Transitions
@@ -84,9 +85,7 @@
 @ExperimentalCoroutinesApi
 class DesktopTasksLimiterTest : ShellTestCase() {
 
-    @JvmField
-    @Rule
-    val setFlagsRule = SetFlagsRule()
+    @JvmField @Rule val setFlagsRule = SetFlagsRule()
 
     @Mock lateinit var shellTaskOrganizer: ShellTaskOrganizer
     @Mock lateinit var transitions: Transitions
@@ -96,6 +95,7 @@
     @Mock lateinit var persistentRepository: DesktopPersistentRepository
     @Mock lateinit var repositoryInitializer: DesktopRepositoryInitializer
     @Mock lateinit var userManager: UserManager
+    @Mock lateinit var shellController: ShellController
 
     private lateinit var mockitoSession: StaticMockitoSession
     private lateinit var desktopTasksLimiter: DesktopTasksLimiter
@@ -106,9 +106,12 @@
 
     @Before
     fun setUp() {
-        mockitoSession = ExtendedMockito.mockitoSession().strictness(Strictness.LENIENT)
-                .spyStatic(DesktopModeStatus::class.java).startMocking()
-        doReturn(true).`when`{ DesktopModeStatus.canEnterDesktopMode(any()) }
+        mockitoSession =
+            ExtendedMockito.mockitoSession()
+                .strictness(Strictness.LENIENT)
+                .spyStatic(DesktopModeStatus::class.java)
+                .startMocking()
+        doReturn(true).`when` { DesktopModeStatus.canEnterDesktopMode(any()) }
         shellInit = spy(ShellInit(testExecutor))
         Dispatchers.setMain(StandardTestDispatcher())
         testScope = CoroutineScope(Dispatchers.Unconfined + SupervisorJob())
@@ -117,15 +120,23 @@
             DesktopUserRepositories(
                 context,
                 shellInit,
+                shellController,
                 persistentRepository,
                 repositoryInitializer,
                 testScope,
-                userManager
+                userManager,
             )
         desktopTaskRepo = userRepositories.current
         desktopTasksLimiter =
-            DesktopTasksLimiter(transitions, userRepositories, shellTaskOrganizer, MAX_TASK_LIMIT,
-                interactionJankMonitor, mContext, handler)
+            DesktopTasksLimiter(
+                transitions,
+                userRepositories,
+                shellTaskOrganizer,
+                MAX_TASK_LIMIT,
+                interactionJankMonitor,
+                mContext,
+                handler,
+            )
     }
 
     @After
@@ -137,16 +148,30 @@
     @Test
     fun createDesktopTasksLimiter_withZeroLimit_shouldThrow() {
         assertFailsWith<IllegalArgumentException> {
-            DesktopTasksLimiter(transitions, userRepositories, shellTaskOrganizer, 0,
-                interactionJankMonitor, mContext, handler)
+            DesktopTasksLimiter(
+                transitions,
+                userRepositories,
+                shellTaskOrganizer,
+                0,
+                interactionJankMonitor,
+                mContext,
+                handler,
+            )
         }
     }
 
     @Test
     fun createDesktopTasksLimiter_withNegativeLimit_shouldThrow() {
         assertFailsWith<IllegalArgumentException> {
-            DesktopTasksLimiter(transitions, userRepositories, shellTaskOrganizer, -5,
-                interactionJankMonitor, mContext, handler)
+            DesktopTasksLimiter(
+                transitions,
+                userRepositories,
+                shellTaskOrganizer,
+                -5,
+                interactionJankMonitor,
+                mContext,
+                handler,
+            )
         }
     }
 
@@ -165,11 +190,14 @@
         val task = setUpFreeformTask()
         markTaskHidden(task)
 
-        desktopTasksLimiter.getTransitionObserver().onTransitionReady(
+        desktopTasksLimiter
+            .getTransitionObserver()
+            .onTransitionReady(
                 Binder() /* transition */,
                 TransitionInfoBuilder(TRANSIT_OPEN).addChange(TRANSIT_TO_BACK, task).build(),
                 StubTransaction() /* startTransaction */,
-                StubTransaction() /* finishTransaction */)
+                StubTransaction(), /* finishTransaction */
+            )
 
         assertThat(desktopTaskRepo.isMinimizedTask(taskId = task.taskId)).isFalse()
     }
@@ -181,13 +209,19 @@
         val task = setUpFreeformTask()
         markTaskHidden(task)
         desktopTasksLimiter.addPendingMinimizeChange(
-            pendingTransition, displayId = DEFAULT_DISPLAY, taskId = task.taskId)
+            pendingTransition,
+            displayId = DEFAULT_DISPLAY,
+            taskId = task.taskId,
+        )
 
-        desktopTasksLimiter.getTransitionObserver().onTransitionReady(
-            taskTransition /* transition */,
-            TransitionInfoBuilder(TRANSIT_OPEN).addChange(TRANSIT_TO_BACK, task).build(),
-            StubTransaction() /* startTransaction */,
-            StubTransaction() /* finishTransaction */)
+        desktopTasksLimiter
+            .getTransitionObserver()
+            .onTransitionReady(
+                taskTransition /* transition */,
+                TransitionInfoBuilder(TRANSIT_OPEN).addChange(TRANSIT_TO_BACK, task).build(),
+                StubTransaction() /* startTransaction */,
+                StubTransaction(), /* finishTransaction */
+            )
 
         assertThat(desktopTaskRepo.isMinimizedTask(taskId = task.taskId)).isFalse()
     }
@@ -198,13 +232,19 @@
         val task = setUpFreeformTask()
         markTaskVisible(task)
         desktopTasksLimiter.addPendingMinimizeChange(
-                transition, displayId = DEFAULT_DISPLAY, taskId = task.taskId)
+            transition,
+            displayId = DEFAULT_DISPLAY,
+            taskId = task.taskId,
+        )
 
-        desktopTasksLimiter.getTransitionObserver().onTransitionReady(
+        desktopTasksLimiter
+            .getTransitionObserver()
+            .onTransitionReady(
                 transition,
                 TransitionInfoBuilder(TRANSIT_OPEN).build(),
                 StubTransaction() /* startTransaction */,
-                StubTransaction() /* finishTransaction */)
+                StubTransaction(), /* finishTransaction */
+            )
 
         assertThat(desktopTaskRepo.isMinimizedTask(taskId = task.taskId)).isFalse()
     }
@@ -215,13 +255,19 @@
         val task = setUpFreeformTask()
         markTaskHidden(task)
         desktopTasksLimiter.addPendingMinimizeChange(
-                transition, displayId = DEFAULT_DISPLAY, taskId = task.taskId)
+            transition,
+            displayId = DEFAULT_DISPLAY,
+            taskId = task.taskId,
+        )
 
-        desktopTasksLimiter.getTransitionObserver().onTransitionReady(
+        desktopTasksLimiter
+            .getTransitionObserver()
+            .onTransitionReady(
                 transition,
                 TransitionInfoBuilder(TRANSIT_OPEN).build(),
                 StubTransaction() /* startTransaction */,
-                StubTransaction() /* finishTransaction */)
+                StubTransaction(), /* finishTransaction */
+            )
 
         assertThat(desktopTaskRepo.isMinimizedTask(taskId = task.taskId)).isTrue()
     }
@@ -231,13 +277,19 @@
         val transition = Binder()
         val task = setUpFreeformTask()
         desktopTasksLimiter.addPendingMinimizeChange(
-                transition, displayId = DEFAULT_DISPLAY, taskId = task.taskId)
+            transition,
+            displayId = DEFAULT_DISPLAY,
+            taskId = task.taskId,
+        )
 
-        desktopTasksLimiter.getTransitionObserver().onTransitionReady(
+        desktopTasksLimiter
+            .getTransitionObserver()
+            .onTransitionReady(
                 transition,
                 TransitionInfoBuilder(TRANSIT_OPEN).addChange(TRANSIT_TO_BACK, task).build(),
                 StubTransaction() /* startTransaction */,
-                StubTransaction() /* finishTransaction */)
+                StubTransaction(), /* finishTransaction */
+            )
 
         assertThat(desktopTaskRepo.isMinimizedTask(taskId = task.taskId)).isTrue()
     }
@@ -248,22 +300,29 @@
         val transition = Binder()
         val task = setUpFreeformTask()
         desktopTasksLimiter.addPendingMinimizeChange(
-            transition, displayId = DEFAULT_DISPLAY, taskId = task.taskId)
-
-        val change = TransitionInfo.Change(task.token, mock(SurfaceControl::class.java)).apply {
-            mode = TRANSIT_TO_BACK
-            taskInfo = task
-            setStartAbsBounds(bounds)
-        }
-        desktopTasksLimiter.getTransitionObserver().onTransitionReady(
             transition,
-            TransitionInfo(TRANSIT_OPEN, TransitionInfo.FLAG_NONE).apply { addChange(change) },
-            StubTransaction() /* startTransaction */,
-            StubTransaction() /* finishTransaction */)
+            displayId = DEFAULT_DISPLAY,
+            taskId = task.taskId,
+        )
+
+        val change =
+            TransitionInfo.Change(task.token, mock(SurfaceControl::class.java)).apply {
+                mode = TRANSIT_TO_BACK
+                taskInfo = task
+                setStartAbsBounds(bounds)
+            }
+        desktopTasksLimiter
+            .getTransitionObserver()
+            .onTransitionReady(
+                transition,
+                TransitionInfo(TRANSIT_OPEN, TransitionInfo.FLAG_NONE).apply { addChange(change) },
+                StubTransaction() /* startTransaction */,
+                StubTransaction(), /* finishTransaction */
+            )
 
         assertThat(desktopTaskRepo.isMinimizedTask(taskId = task.taskId)).isTrue()
-        assertThat(desktopTaskRepo.removeBoundsBeforeMinimize(taskId = task.taskId)).isEqualTo(
-            bounds)
+        assertThat(desktopTaskRepo.removeBoundsBeforeMinimize(taskId = task.taskId))
+            .isEqualTo(bounds)
     }
 
     @Test
@@ -272,15 +331,22 @@
         val newTransition = Binder()
         val task = setUpFreeformTask()
         desktopTasksLimiter.addPendingMinimizeChange(
-            mergedTransition, displayId = DEFAULT_DISPLAY, taskId = task.taskId)
-        desktopTasksLimiter.getTransitionObserver().onTransitionMerged(
-            mergedTransition, newTransition)
+            mergedTransition,
+            displayId = DEFAULT_DISPLAY,
+            taskId = task.taskId,
+        )
+        desktopTasksLimiter
+            .getTransitionObserver()
+            .onTransitionMerged(mergedTransition, newTransition)
 
-        desktopTasksLimiter.getTransitionObserver().onTransitionReady(
-            newTransition,
-            TransitionInfoBuilder(TRANSIT_OPEN).addChange(TRANSIT_TO_BACK, task).build(),
-            StubTransaction() /* startTransaction */,
-            StubTransaction() /* finishTransaction */)
+        desktopTasksLimiter
+            .getTransitionObserver()
+            .onTransitionReady(
+                newTransition,
+                TransitionInfoBuilder(TRANSIT_OPEN).addChange(TRANSIT_TO_BACK, task).build(),
+                StubTransaction() /* startTransaction */,
+                StubTransaction(), /* finishTransaction */
+            )
 
         assertThat(desktopTaskRepo.isMinimizedTask(taskId = task.taskId)).isTrue()
     }
@@ -294,7 +360,9 @@
 
         val wct = WindowContainerTransaction()
         desktopTasksLimiter.leftoverMinimizedTasksRemover.removeLeftoverMinimizedTasks(
-            DEFAULT_DISPLAY, wct)
+            DEFAULT_DISPLAY,
+            wct,
+        )
 
         assertThat(wct.isEmpty).isTrue()
     }
@@ -304,7 +372,9 @@
     fun removeLeftoverMinimizedTasks_noMinimizedTasks_doesNothing() {
         val wct = WindowContainerTransaction()
         desktopTasksLimiter.leftoverMinimizedTasksRemover.removeLeftoverMinimizedTasks(
-            DEFAULT_DISPLAY, wct)
+            DEFAULT_DISPLAY,
+            wct,
+        )
 
         assertThat(wct.isEmpty).isTrue()
     }
@@ -319,7 +389,9 @@
 
         val wct = WindowContainerTransaction()
         desktopTasksLimiter.leftoverMinimizedTasksRemover.removeLeftoverMinimizedTasks(
-            DEFAULT_DISPLAY, wct)
+            DEFAULT_DISPLAY,
+            wct,
+        )
 
         assertThat(wct.hierarchyOps).hasSize(2)
         assertThat(wct.hierarchyOps[0].type).isEqualTo(HIERARCHY_OP_TYPE_REMOVE_TASK)
@@ -348,10 +420,11 @@
 
         val wct = WindowContainerTransaction()
         val minimizedTaskId =
-                desktopTasksLimiter.addAndGetMinimizeTaskChanges(
-                        displayId = DEFAULT_DISPLAY,
-                        wct = wct,
-                        newFrontTaskId = setUpFreeformTask().taskId)
+            desktopTasksLimiter.addAndGetMinimizeTaskChanges(
+                displayId = DEFAULT_DISPLAY,
+                wct = wct,
+                newFrontTaskId = setUpFreeformTask().taskId,
+            )
 
         assertThat(minimizedTaskId).isNull()
         assertThat(wct.hierarchyOps).isEmpty() // No reordering operations added
@@ -364,10 +437,11 @@
 
         val wct = WindowContainerTransaction()
         val minimizedTaskId =
-                desktopTasksLimiter.addAndGetMinimizeTaskChanges(
-                        displayId = DEFAULT_DISPLAY,
-                        wct = wct,
-                        newFrontTaskId = setUpFreeformTask().taskId)
+            desktopTasksLimiter.addAndGetMinimizeTaskChanges(
+                displayId = DEFAULT_DISPLAY,
+                wct = wct,
+                newFrontTaskId = setUpFreeformTask().taskId,
+            )
 
         assertThat(minimizedTaskId).isEqualTo(tasks.first().taskId)
         assertThat(wct.hierarchyOps.size).isEqualTo(1)
@@ -382,10 +456,11 @@
 
         val wct = WindowContainerTransaction()
         val minimizedTaskId =
-                desktopTasksLimiter.addAndGetMinimizeTaskChanges(
-                        displayId = 0,
-                        wct = wct,
-                        newFrontTaskId = setUpFreeformTask().taskId)
+            desktopTasksLimiter.addAndGetMinimizeTaskChanges(
+                displayId = 0,
+                wct = wct,
+                newFrontTaskId = setUpFreeformTask().taskId,
+            )
 
         assertThat(minimizedTaskId).isNull()
         assertThat(wct.hierarchyOps).isEmpty() // No reordering operations added
@@ -395,8 +470,8 @@
     fun getTaskToMinimize_tasksWithinLimit_returnsNull() {
         val tasks = (1..MAX_TASK_LIMIT).map { setUpFreeformTask() }
 
-        val minimizedTask = desktopTasksLimiter.getTaskIdToMinimize(
-                visibleOrderedTasks = tasks.map { it.taskId })
+        val minimizedTask =
+            desktopTasksLimiter.getTaskIdToMinimize(visibleOrderedTasks = tasks.map { it.taskId })
 
         assertThat(minimizedTask).isNull()
     }
@@ -405,8 +480,8 @@
     fun getTaskToMinimize_tasksAboveLimit_returnsBackTask() {
         val tasks = (1..MAX_TASK_LIMIT + 1).map { setUpFreeformTask() }
 
-        val minimizedTask = desktopTasksLimiter.getTaskIdToMinimize(
-                visibleOrderedTasks = tasks.map { it.taskId })
+        val minimizedTask =
+            desktopTasksLimiter.getTaskIdToMinimize(visibleOrderedTasks = tasks.map { it.taskId })
 
         // first == front, last == back
         assertThat(minimizedTask).isEqualTo(tasks.last().taskId)
@@ -415,12 +490,19 @@
     @Test
     fun getTaskToMinimize_tasksAboveLimit_otherLimit_returnsBackTask() {
         desktopTasksLimiter =
-            DesktopTasksLimiter(transitions, userRepositories, shellTaskOrganizer, MAX_TASK_LIMIT2,
-                interactionJankMonitor, mContext, handler)
+            DesktopTasksLimiter(
+                transitions,
+                userRepositories,
+                shellTaskOrganizer,
+                MAX_TASK_LIMIT2,
+                interactionJankMonitor,
+                mContext,
+                handler,
+            )
         val tasks = (1..MAX_TASK_LIMIT2 + 1).map { setUpFreeformTask() }
 
-        val minimizedTask = desktopTasksLimiter.getTaskIdToMinimize(
-            visibleOrderedTasks = tasks.map { it.taskId })
+        val minimizedTask =
+            desktopTasksLimiter.getTaskIdToMinimize(visibleOrderedTasks = tasks.map { it.taskId })
 
         // first == front, last == back
         assertThat(minimizedTask).isEqualTo(tasks.last().taskId)
@@ -430,9 +512,25 @@
     fun getTaskToMinimize_withNewTask_tasksAboveLimit_returnsBackTask() {
         val tasks = (1..MAX_TASK_LIMIT).map { setUpFreeformTask() }
 
-        val minimizedTask = desktopTasksLimiter.getTaskIdToMinimize(
+        val minimizedTask =
+            desktopTasksLimiter.getTaskIdToMinimize(
                 visibleOrderedTasks = tasks.map { it.taskId },
-                newTaskIdInFront = setUpFreeformTask().taskId)
+                newTaskIdInFront = setUpFreeformTask().taskId,
+            )
+
+        // first == front, last == back
+        assertThat(minimizedTask).isEqualTo(tasks.last().taskId)
+    }
+
+    @Test
+    fun getTaskToMinimize_tasksAtLimit_newIntentReturnsBackTask() {
+        val tasks = (1..MAX_TASK_LIMIT).map { setUpFreeformTask() }
+        val minimizedTask =
+            desktopTasksLimiter.getTaskIdToMinimize(
+                visibleOrderedTasks = tasks.map { it.taskId },
+                newTaskIdInFront = null,
+                launchingNewIntent = true,
+            )
 
         // first == front, last == back
         assertThat(minimizedTask).isEqualTo(tasks.last().taskId)
@@ -444,25 +542,28 @@
         val transition = Binder()
         val task = setUpFreeformTask()
         desktopTasksLimiter.addPendingMinimizeChange(
-            transition, displayId = DEFAULT_DISPLAY, taskId = task.taskId)
-
-        desktopTasksLimiter.getTransitionObserver().onTransitionReady(
             transition,
-            TransitionInfoBuilder(TRANSIT_OPEN).build(),
-            StubTransaction() /* startTransaction */,
-            StubTransaction() /* finishTransaction */)
+            displayId = DEFAULT_DISPLAY,
+            taskId = task.taskId,
+        )
+
+        desktopTasksLimiter
+            .getTransitionObserver()
+            .onTransitionReady(
+                transition,
+                TransitionInfoBuilder(TRANSIT_OPEN).build(),
+                StubTransaction() /* startTransaction */,
+                StubTransaction(), /* finishTransaction */
+            )
 
         desktopTasksLimiter.getTransitionObserver().onTransitionStarting(transition)
 
-        verify(interactionJankMonitor).begin(
-            any(),
-            eq(mContext),
-            eq(handler),
-            eq(CUJ_DESKTOP_MODE_MINIMIZE_WINDOW))
+        verify(interactionJankMonitor)
+            .begin(any(), eq(mContext), eq(handler), eq(CUJ_DESKTOP_MODE_MINIMIZE_WINDOW))
 
-        desktopTasksLimiter.getTransitionObserver().onTransitionFinished(
-            transition,
-            /* aborted = */ false)
+        desktopTasksLimiter
+            .getTransitionObserver()
+            .onTransitionFinished(transition, /* aborted= */ false)
 
         verify(interactionJankMonitor).end(eq(CUJ_DESKTOP_MODE_MINIMIZE_WINDOW))
     }
@@ -473,26 +574,28 @@
         val transition = Binder()
         val task = setUpFreeformTask()
         desktopTasksLimiter.addPendingMinimizeChange(
-            transition, displayId = DEFAULT_DISPLAY, taskId = task.taskId)
-
-        desktopTasksLimiter.getTransitionObserver().onTransitionReady(
             transition,
-            TransitionInfoBuilder(TRANSIT_OPEN).build(),
-            StubTransaction() /* startTransaction */,
-            StubTransaction() /* finishTransaction */)
+            displayId = DEFAULT_DISPLAY,
+            taskId = task.taskId,
+        )
+
+        desktopTasksLimiter
+            .getTransitionObserver()
+            .onTransitionReady(
+                transition,
+                TransitionInfoBuilder(TRANSIT_OPEN).build(),
+                StubTransaction() /* startTransaction */,
+                StubTransaction(), /* finishTransaction */
+            )
 
         desktopTasksLimiter.getTransitionObserver().onTransitionStarting(transition)
 
-        verify(interactionJankMonitor).begin(
-            any(),
-            eq(mContext),
-            eq(handler),
-            eq(CUJ_DESKTOP_MODE_MINIMIZE_WINDOW),
-        )
+        verify(interactionJankMonitor)
+            .begin(any(), eq(mContext), eq(handler), eq(CUJ_DESKTOP_MODE_MINIMIZE_WINDOW))
 
-        desktopTasksLimiter.getTransitionObserver().onTransitionFinished(
-            transition,
-            /* aborted = */ true)
+        desktopTasksLimiter
+            .getTransitionObserver()
+            .onTransitionFinished(transition, /* aborted= */ true)
 
         verify(interactionJankMonitor).cancel(eq(CUJ_DESKTOP_MODE_MINIMIZE_WINDOW))
     }
@@ -504,25 +607,28 @@
         val newTransition = Binder()
         val task = setUpFreeformTask()
         desktopTasksLimiter.addPendingMinimizeChange(
-            mergedTransition, displayId = DEFAULT_DISPLAY, taskId = task.taskId)
-
-        desktopTasksLimiter.getTransitionObserver().onTransitionReady(
             mergedTransition,
-            TransitionInfoBuilder(TRANSIT_OPEN).build(),
-            StubTransaction() /* startTransaction */,
-            StubTransaction() /* finishTransaction */)
+            displayId = DEFAULT_DISPLAY,
+            taskId = task.taskId,
+        )
+
+        desktopTasksLimiter
+            .getTransitionObserver()
+            .onTransitionReady(
+                mergedTransition,
+                TransitionInfoBuilder(TRANSIT_OPEN).build(),
+                StubTransaction() /* startTransaction */,
+                StubTransaction(), /* finishTransaction */
+            )
 
         desktopTasksLimiter.getTransitionObserver().onTransitionStarting(mergedTransition)
 
-        verify(interactionJankMonitor).begin(
-            any(),
-            eq(mContext),
-            eq(handler),
-            eq(CUJ_DESKTOP_MODE_MINIMIZE_WINDOW))
+        verify(interactionJankMonitor)
+            .begin(any(), eq(mContext), eq(handler), eq(CUJ_DESKTOP_MODE_MINIMIZE_WINDOW))
 
-        desktopTasksLimiter.getTransitionObserver().onTransitionMerged(
-            mergedTransition,
-            newTransition)
+        desktopTasksLimiter
+            .getTransitionObserver()
+            .onTransitionMerged(mergedTransition, newTransition)
 
         verify(interactionJankMonitor).end(eq(CUJ_DESKTOP_MODE_MINIMIZE_WINDOW))
     }
@@ -535,19 +641,11 @@
     }
 
     private fun markTaskVisible(task: RunningTaskInfo) {
-        desktopTaskRepo.updateTask(
-                task.displayId,
-                task.taskId,
-                isVisible = true
-        )
+        desktopTaskRepo.updateTask(task.displayId, task.taskId, isVisible = true)
     }
 
     private fun markTaskHidden(task: RunningTaskInfo) {
-        desktopTaskRepo.updateTask(
-                task.displayId,
-                task.taskId,
-                isVisible = false
-        )
+        desktopTaskRepo.updateTask(task.displayId, task.taskId, isVisible = false)
     }
 
     private companion object {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt
index b31a3f5..c66d203 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt
@@ -24,6 +24,7 @@
 import android.content.Intent
 import android.os.IBinder
 import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.SetFlagsRule
 import android.view.Display.DEFAULT_DISPLAY
 import android.view.WindowManager
 import android.view.WindowManager.TRANSIT_CLOSE
@@ -63,8 +64,15 @@
 import org.mockito.kotlin.verify
 import org.mockito.kotlin.whenever
 
+/**
+ * Tests for [@link DesktopTasksTransitionObserver].
+ *
+ * Build/Install/Run: atest WMShellUnitTests:DesktopTasksTransitionObserverTest
+ */
 class DesktopTasksTransitionObserverTest {
 
+    @JvmField @Rule val setFlagsRule = SetFlagsRule()
+
     @JvmField
     @Rule
     val extendedMockitoRule =
@@ -99,7 +107,7 @@
                 shellTaskOrganizer,
                 mixedHandler,
                 backAnimationController,
-                shellInit
+                shellInit,
             )
     }
 
@@ -139,7 +147,10 @@
         verify(taskRepository).minimizeTask(task.displayId, task.taskId)
         val pendingTransition =
             DesktopMixedTransitionHandler.PendingMixedTransition.Minimize(
-                transition, task.taskId, isLastTask = false)
+                transition,
+                task.taskId,
+                isLastTask = false,
+            )
         verify(mixedHandler).addPendingMixedTransition(pendingTransition)
     }
 
@@ -162,7 +173,10 @@
         verify(taskRepository).minimizeTask(task.displayId, task.taskId)
         val pendingTransition =
             DesktopMixedTransitionHandler.PendingMixedTransition.Minimize(
-                transition, task.taskId, isLastTask = true)
+                transition,
+                task.taskId,
+                isLastTask = true,
+            )
         verify(mixedHandler).addPendingMixedTransition(pendingTransition)
     }
 
@@ -239,6 +253,48 @@
         wct.assertRemoveAt(index = 0, wallpaperToken)
     }
 
+    @Test
+    @EnableFlags(
+        Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY,
+        Flags.FLAG_INCLUDE_TOP_TRANSPARENT_FULLSCREEN_TASK_IN_DESKTOP_HEURISTIC,
+    )
+    fun topTransparentTaskClosed_clearTaskIdFromRepository() {
+        val mockTransition = Mockito.mock(IBinder::class.java)
+        val topTransparentTask = createTaskInfo(1)
+        whenever(taskRepository.getTopTransparentFullscreenTaskId(any()))
+            .thenReturn(topTransparentTask.taskId)
+
+        transitionObserver.onTransitionReady(
+            transition = mockTransition,
+            info = createCloseTransition(topTransparentTask),
+            startTransaction = mock(),
+            finishTransaction = mock(),
+        )
+
+        verify(taskRepository).clearTopTransparentFullscreenTaskId(topTransparentTask.displayId)
+    }
+
+    @Test
+    @EnableFlags(
+        Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY,
+        Flags.FLAG_INCLUDE_TOP_TRANSPARENT_FULLSCREEN_TASK_IN_DESKTOP_HEURISTIC,
+    )
+    fun topTransparentTaskSentToBack_clearTaskIdFromRepository() {
+        val mockTransition = Mockito.mock(IBinder::class.java)
+        val topTransparentTask = createTaskInfo(1)
+        whenever(taskRepository.getTopTransparentFullscreenTaskId(any()))
+            .thenReturn(topTransparentTask.taskId)
+
+        transitionObserver.onTransitionReady(
+            transition = mockTransition,
+            info = createToBackTransition(topTransparentTask),
+            startTransaction = mock(),
+            finishTransaction = mock(),
+        )
+
+        verify(taskRepository).clearTopTransparentFullscreenTaskId(topTransparentTask.displayId)
+    }
+
     private fun createBackNavigationTransition(
         task: RunningTaskInfo?,
         type: Int = TRANSIT_TO_BACK,
@@ -251,7 +307,8 @@
                     parent = null
                     taskInfo = task
                     flags = flags
-                })
+                }
+            )
             if (withWallpaper) {
                 addChange(
                     Change(mock(), mock()).apply {
@@ -259,14 +316,15 @@
                         parent = null
                         taskInfo = createWallpaperTaskInfo()
                         flags = flags
-                    })
+                    }
+                )
             }
         }
     }
 
     private fun createOpenChangeTransition(
         task: RunningTaskInfo?,
-        type: Int = TRANSIT_OPEN
+        type: Int = TRANSIT_OPEN,
     ): TransitionInfo {
         return TransitionInfo(TRANSIT_OPEN, 0 /* flags */).apply {
             addChange(
@@ -275,7 +333,8 @@
                     parent = null
                     taskInfo = task
                     flags = flags
-                })
+                }
+            )
         }
     }
 
@@ -287,13 +346,27 @@
                     parent = null
                     taskInfo = task
                     flags = flags
-                })
+                }
+            )
+        }
+    }
+
+    private fun createToBackTransition(task: RunningTaskInfo?): TransitionInfo {
+        return TransitionInfo(TRANSIT_TO_BACK, 0 /* flags */).apply {
+            addChange(
+                Change(mock(), mock()).apply {
+                    mode = TRANSIT_TO_BACK
+                    parent = null
+                    taskInfo = task
+                    flags = flags
+                }
+            )
         }
     }
 
     private fun getLatestWct(
         @WindowManager.TransitionType type: Int = TRANSIT_OPEN,
-        handlerClass: Class<out Transitions.TransitionHandler>? = null
+        handlerClass: Class<out Transitions.TransitionHandler>? = null,
     ): WindowContainerTransaction {
         val arg = ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
         if (handlerClass == null) {
@@ -330,8 +403,6 @@
         RunningTaskInfo().apply {
             token = mock<WindowContainerToken>()
             baseIntent =
-                Intent().apply {
-                    component = DesktopWallpaperActivity.wallpaperActivityComponent
-                }
+                Intent().apply { component = DesktopWallpaperActivity.wallpaperActivityComponent }
         }
 }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopUserRepositoriesTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopUserRepositoriesTest.kt
index 5767df4..b9e307fa5 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopUserRepositoriesTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopUserRepositoriesTest.kt
@@ -24,14 +24,15 @@
 import android.platform.test.flag.junit.SetFlagsRule
 import android.testing.AndroidTestingRunner
 import androidx.test.filters.SmallTest
-import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
 import com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn
+import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
 import com.android.dx.mockito.inline.extended.StaticMockitoSession
 import com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_HSUM
 import com.android.wm.shell.ShellTestCase
 import com.android.wm.shell.common.ShellExecutor
 import com.android.wm.shell.desktopmode.persistence.DesktopPersistentRepository
 import com.android.wm.shell.desktopmode.persistence.DesktopRepositoryInitializer
+import com.android.wm.shell.sysui.ShellController
 import com.android.wm.shell.sysui.ShellInit
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.CoroutineScope
@@ -66,6 +67,7 @@
     private val persistentRepository = mock<DesktopPersistentRepository>()
     private val repositoryInitializer = mock<DesktopRepositoryInitializer>()
     private val userManager = mock<UserManager>()
+    private val shellController = mock<ShellController>()
 
     @Before
     fun setUp() {
@@ -80,14 +82,20 @@
         datastoreScope = CoroutineScope(Dispatchers.Unconfined + SupervisorJob())
         shellInit = spy(ShellInit(testExecutor))
 
-        val profiles: MutableList<UserInfo> = mutableListOf(
-            UserInfo(USER_ID_1, "User 1", 0),
-            UserInfo(PROFILE_ID_2, "Profile 2", 0))
+        val profiles: MutableList<UserInfo> =
+            mutableListOf(UserInfo(USER_ID_1, "User 1", 0), UserInfo(PROFILE_ID_2, "Profile 2", 0))
         whenever(userManager.getProfiles(USER_ID_1)).thenReturn(profiles)
 
-        userRepositories = DesktopUserRepositories(
-            context, shellInit, persistentRepository, repositoryInitializer, datastoreScope,
-                userManager)
+        userRepositories =
+            DesktopUserRepositories(
+                context,
+                shellInit,
+                shellController,
+                persistentRepository,
+                repositoryInitializer,
+                datastoreScope,
+                userManager,
+            )
     }
 
     @After
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt
index 13528b9..e4eff9f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt
@@ -61,9 +61,7 @@
 @RunWithLooper
 @RunWith(AndroidTestingRunner::class)
 class DragToDesktopTransitionHandlerTest : ShellTestCase() {
-    @JvmField
-    @Rule
-    val mAnimatorTestRule = AnimatorTestRule(this)
+    @JvmField @Rule val mAnimatorTestRule = AnimatorTestRule(this)
 
     @Mock private lateinit var transitions: Transitions
     @Mock private lateinit var taskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer
@@ -123,11 +121,11 @@
             info =
                 createTransitionInfo(
                     type = TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP,
-                    draggedTask = task
+                    draggedTask = task,
                 ),
             startTransaction = mock(),
             finishTransaction = mock(),
-            finishCallback = {}
+            finishCallback = {},
         )
 
         verify(dragAnimator).startAnimation()
@@ -137,13 +135,13 @@
     fun startDragToDesktop_cancelledBeforeReady_startCancelTransition() {
         performEarlyCancel(
             defaultHandler,
-            DragToDesktopTransitionHandler.CancelState.STANDARD_CANCEL
+            DragToDesktopTransitionHandler.CancelState.STANDARD_CANCEL,
         )
         verify(transitions)
             .startTransition(
                 eq(TRANSIT_DESKTOP_MODE_CANCEL_DRAG_TO_DESKTOP),
                 any(),
-                eq(defaultHandler)
+                eq(defaultHandler),
             )
     }
 
@@ -151,7 +149,7 @@
     fun startDragToDesktop_cancelledBeforeReady_verifySplitLeftCancel() {
         performEarlyCancel(
             defaultHandler,
-            DragToDesktopTransitionHandler.CancelState.CANCEL_SPLIT_LEFT
+            DragToDesktopTransitionHandler.CancelState.CANCEL_SPLIT_LEFT,
         )
         verify(splitScreenController)
             .requestEnterSplitSelect(any(), any(), eq(SPLIT_POSITION_TOP_OR_LEFT), any())
@@ -161,7 +159,7 @@
     fun startDragToDesktop_cancelledBeforeReady_verifySplitRightCancel() {
         performEarlyCancel(
             defaultHandler,
-            DragToDesktopTransitionHandler.CancelState.CANCEL_SPLIT_RIGHT
+            DragToDesktopTransitionHandler.CancelState.CANCEL_SPLIT_RIGHT,
         )
         verify(splitScreenController)
             .requestEnterSplitSelect(any(), any(), eq(SPLIT_POSITION_BOTTOM_OR_RIGHT), any())
@@ -214,7 +212,7 @@
             .startTransition(
                 eq(TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP),
                 any(),
-                eq(defaultHandler)
+                eq(defaultHandler),
             )
     }
 
@@ -277,14 +275,18 @@
         val startToken = startDrag(defaultHandler)
 
         // Then user cancelled after it had already started.
-        val cancelToken = cancelDragToDesktopTransition(
-            defaultHandler, DragToDesktopTransitionHandler.CancelState.STANDARD_CANCEL)
+        val cancelToken =
+            cancelDragToDesktopTransition(
+                defaultHandler,
+                DragToDesktopTransitionHandler.CancelState.STANDARD_CANCEL,
+            )
         defaultHandler.mergeAnimation(
             cancelToken,
             TransitionInfo(TRANSIT_DESKTOP_MODE_CANCEL_DRAG_TO_DESKTOP, 0),
             mock<SurfaceControl.Transaction>(),
             startToken,
-            mock<Transitions.TransitionFinishCallback>())
+            mock<Transitions.TransitionFinishCallback>(),
+        )
 
         // Cancel animation should run since it had already started.
         verify(dragAnimator).cancelAnimator()
@@ -296,8 +298,11 @@
         val startToken = startDrag(defaultHandler)
 
         // Then user cancelled after it had already started.
-        val cancelToken = cancelDragToDesktopTransition(
-            defaultHandler, DragToDesktopTransitionHandler.CancelState.STANDARD_CANCEL)
+        val cancelToken =
+            cancelDragToDesktopTransition(
+                defaultHandler,
+                DragToDesktopTransitionHandler.CancelState.STANDARD_CANCEL,
+            )
         defaultHandler.onTransitionConsumed(cancelToken, aborted = true, null)
 
         // Cancel animation should run since it had already started.
@@ -360,7 +365,7 @@
             .startTransition(
                 eq(TRANSIT_DESKTOP_MODE_CANCEL_DRAG_TO_DESKTOP),
                 any(),
-                eq(defaultHandler)
+                eq(defaultHandler),
             )
     }
 
@@ -374,7 +379,7 @@
             .startTransition(
                 eq(TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP),
                 any(),
-                eq(defaultHandler)
+                eq(defaultHandler),
             )
     }
 
@@ -390,7 +395,7 @@
             info = createTransitionInfo(type = TRANSIT_OPEN, draggedTask = task),
             t = transaction,
             mergeTarget = mock(),
-            finishCallback = finishCallback
+            finishCallback = finishCallback,
         )
 
         // Should NOT have any transaction changes
@@ -414,11 +419,11 @@
             info =
                 createTransitionInfo(
                     type = TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP,
-                    draggedTask = task
+                    draggedTask = task,
                 ),
             t = mergedStartTransaction,
             mergeTarget = startTransition,
-            finishCallback = finishCallback
+            finishCallback = finishCallback,
         )
 
         // Should show dragged task layer in start and finish transaction
@@ -446,11 +451,11 @@
             info =
                 createTransitionInfo(
                     type = TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP,
-                    draggedTask = task
+                    draggedTask = task,
                 ),
             t = mergedStartTransaction,
             mergeTarget = startTransition,
-            finishCallback = finishCallback
+            finishCallback = finishCallback,
         )
 
         // Should show dragged task layer in start and finish transaction
@@ -475,7 +480,7 @@
         assertEquals(
             "Expects to return system properties stored value",
             /* expected= */ value,
-            /* actual= */ SpringDragToDesktopTransitionHandler.propertyValue(name)
+            /* actual= */ SpringDragToDesktopTransitionHandler.propertyValue(name),
         )
     }
 
@@ -491,7 +496,7 @@
         assertEquals(
             "Expects to return scaled system properties stored value",
             /* expected= */ value / scale,
-            /* actual= */ SpringDragToDesktopTransitionHandler.propertyValue(name, scale = scale)
+            /* actual= */ SpringDragToDesktopTransitionHandler.propertyValue(name, scale = scale),
         )
     }
 
@@ -508,8 +513,8 @@
             /* expected= */ defaultValue,
             /* actual= */ SpringDragToDesktopTransitionHandler.propertyValue(
                 name,
-                default = defaultValue
-            )
+                default = defaultValue,
+            ),
         )
     }
 
@@ -530,8 +535,8 @@
             /* actual= */ SpringDragToDesktopTransitionHandler.propertyValue(
                 name,
                 default = defaultValue,
-                scale = scale
-            )
+                scale = scale,
+            ),
         )
     }
 
@@ -542,8 +547,8 @@
         defaultHandler.onTransitionConsumed(transition, aborted = true, mock())
 
         verify(mockInteractionJankMonitor).cancel(eq(CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_HOLD))
-        verify(mockInteractionJankMonitor, times(0)).cancel(
-            eq(CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_RELEASE))
+        verify(mockInteractionJankMonitor, times(0))
+            .cancel(eq(CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_RELEASE))
     }
 
     @Test
@@ -554,13 +559,14 @@
         defaultHandler.onTaskResizeAnimationListener = mock()
         defaultHandler.mergeAnimation(
             transition = endTransition,
-            info = createTransitionInfo(
-                type = TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP,
-                draggedTask = task
-            ),
+            info =
+                createTransitionInfo(
+                    type = TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP,
+                    draggedTask = task,
+                ),
             t = mock<SurfaceControl.Transaction>(),
             mergeTarget = startTransition,
-            finishCallback = mock<Transitions.TransitionFinishCallback>()
+            finishCallback = mock<Transitions.TransitionFinishCallback>(),
         )
 
         defaultHandler.onTransitionConsumed(endTransition, aborted = true, mock())
@@ -574,7 +580,7 @@
     private fun startDrag(
         handler: DragToDesktopTransitionHandler,
         task: RunningTaskInfo = createTask(),
-        finishTransaction: SurfaceControl.Transaction = mock()
+        finishTransaction: SurfaceControl.Transaction = mock(),
     ): IBinder {
         whenever(dragAnimator.position).thenReturn(PointF())
         // Simulate transition is started and is ready to animate.
@@ -584,11 +590,11 @@
             info =
                 createTransitionInfo(
                     type = TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP,
-                    draggedTask = task
+                    draggedTask = task,
                 ),
             startTransaction = mock(),
             finishTransaction = finishTransaction,
-            finishCallback = {}
+            finishCallback = {},
         )
         return transition
     }
@@ -596,14 +602,14 @@
     private fun startDragToDesktopTransition(
         handler: DragToDesktopTransitionHandler,
         task: RunningTaskInfo,
-        dragAnimator: MoveToDesktopAnimator
+        dragAnimator: MoveToDesktopAnimator,
     ): IBinder {
         val token = mock<IBinder>()
         whenever(
                 transitions.startTransition(
                     eq(TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP),
                     any(),
-                    eq(handler)
+                    eq(handler),
                 )
             )
             .thenReturn(token)
@@ -613,13 +619,14 @@
 
     private fun cancelDragToDesktopTransition(
         handler: DragToDesktopTransitionHandler,
-        cancelState: DragToDesktopTransitionHandler.CancelState): IBinder {
+        cancelState: DragToDesktopTransitionHandler.CancelState,
+    ): IBinder {
         val token = mock<IBinder>()
         whenever(
                 transitions.startTransition(
                     eq(TRANSIT_DESKTOP_MODE_CANCEL_DRAG_TO_DESKTOP),
                     any(),
-                    eq(handler)
+                    eq(handler),
                 )
             )
             .thenReturn(token)
@@ -630,7 +637,7 @@
 
     private fun performEarlyCancel(
         handler: DragToDesktopTransitionHandler,
-        cancelState: DragToDesktopTransitionHandler.CancelState
+        cancelState: DragToDesktopTransitionHandler.CancelState,
     ) {
         val task = createTask()
         // Simulate transition is started and is ready to animate.
@@ -643,11 +650,11 @@
             info =
                 createTransitionInfo(
                     type = TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP,
-                    draggedTask = task
+                    draggedTask = task,
                 ),
             startTransaction = mock(),
             finishTransaction = mock(),
-            finishCallback = {}
+            finishCallback = {},
         )
 
         // Don't even animate the "drag" since it was already cancelled.
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 38c6ed9..e102539 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
@@ -31,67 +31,71 @@
 @SmallTest
 @RunWith(AndroidTestingRunner::class)
 class WindowDecorCaptionHandleRepositoryTest {
-  private lateinit var captionHandleRepository: WindowDecorCaptionHandleRepository
+    private lateinit var captionHandleRepository: WindowDecorCaptionHandleRepository
 
-  @Before
-  fun setUp() {
-    captionHandleRepository = WindowDecorCaptionHandleRepository()
-  }
+    @Before
+    fun setUp() {
+        captionHandleRepository = WindowDecorCaptionHandleRepository()
+    }
 
-  @Test
-  fun initialState_noAction_returnsNoCaption() {
-    // Check the initial value of `captionStateFlow`.
-    assertThat(captionHandleRepository.captionStateFlow.value).isEqualTo(CaptionState.NoCaption)
-  }
+    @Test
+    fun initialState_noAction_returnsNoCaption() {
+        // Check the initial value of `captionStateFlow`.
+        assertThat(captionHandleRepository.captionStateFlow.value).isEqualTo(CaptionState.NoCaption)
+    }
 
-  @Test
-  fun notifyCaptionChange_toAppHandleVisible_updatesStateWithCorrectData() {
-    val taskInfo = createTaskInfo(WINDOWING_MODE_FULLSCREEN, GMAIL_PACKAGE_NAME)
-    val appHandleCaptionState =
-        CaptionState.AppHandle(
-          runningTaskInfo = taskInfo,
-          isHandleMenuExpanded = false,
-          globalAppHandleBounds = Rect(/* left= */ 0, /* top= */ 1, /* right= */ 2, /* bottom= */ 3),
-          isCapturedLinkAvailable = false)
+    @Test
+    fun notifyCaptionChange_toAppHandleVisible_updatesStateWithCorrectData() {
+        val taskInfo = createTaskInfo(WINDOWING_MODE_FULLSCREEN, GMAIL_PACKAGE_NAME)
+        val appHandleCaptionState =
+            CaptionState.AppHandle(
+                runningTaskInfo = taskInfo,
+                isHandleMenuExpanded = false,
+                globalAppHandleBounds =
+                    Rect(/* left= */ 0, /* top= */ 1, /* right= */ 2, /* bottom= */ 3),
+                isCapturedLinkAvailable = false,
+            )
 
-    captionHandleRepository.notifyCaptionChanged(appHandleCaptionState)
+        captionHandleRepository.notifyCaptionChanged(appHandleCaptionState)
 
-    assertThat(captionHandleRepository.captionStateFlow.value).isEqualTo(appHandleCaptionState)
-  }
+        assertThat(captionHandleRepository.captionStateFlow.value).isEqualTo(appHandleCaptionState)
+    }
 
-  @Test
-  fun notifyCaptionChange_toAppChipVisible_updatesStateWithCorrectData() {
-    val taskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM, GMAIL_PACKAGE_NAME)
-    val appHeaderCaptionState =
-        CaptionState.AppHeader(
-          runningTaskInfo = taskInfo,
-          isHeaderMenuExpanded = true,
-          globalAppChipBounds = Rect(/* left= */ 0, /* top= */ 1, /* right= */ 2, /* bottom= */ 3),
-          isCapturedLinkAvailable = false)
+    @Test
+    fun notifyCaptionChange_toAppChipVisible_updatesStateWithCorrectData() {
+        val taskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM, GMAIL_PACKAGE_NAME)
+        val appHeaderCaptionState =
+            CaptionState.AppHeader(
+                runningTaskInfo = taskInfo,
+                isHeaderMenuExpanded = true,
+                globalAppChipBounds =
+                    Rect(/* left= */ 0, /* top= */ 1, /* right= */ 2, /* bottom= */ 3),
+                isCapturedLinkAvailable = false,
+            )
 
-    captionHandleRepository.notifyCaptionChanged(appHeaderCaptionState)
+        captionHandleRepository.notifyCaptionChanged(appHeaderCaptionState)
 
-    assertThat(captionHandleRepository.captionStateFlow.value).isEqualTo(appHeaderCaptionState)
-  }
+        assertThat(captionHandleRepository.captionStateFlow.value).isEqualTo(appHeaderCaptionState)
+    }
 
-  @Test
-  fun notifyCaptionChange_toNoCaption_updatesState() {
-    captionHandleRepository.notifyCaptionChanged(CaptionState.NoCaption)
+    @Test
+    fun notifyCaptionChange_toNoCaption_updatesState() {
+        captionHandleRepository.notifyCaptionChanged(CaptionState.NoCaption)
 
-    assertThat(captionHandleRepository.captionStateFlow.value).isEqualTo(CaptionState.NoCaption)
-  }
+        assertThat(captionHandleRepository.captionStateFlow.value).isEqualTo(CaptionState.NoCaption)
+    }
 
-  private fun createTaskInfo(
-      deviceWindowingMode: Int = WINDOWING_MODE_UNDEFINED,
-      runningTaskPackageName: String = LAUNCHER_PACKAGE_NAME
-  ): RunningTaskInfo =
-      RunningTaskInfo().apply {
-        configuration.windowConfiguration.apply { windowingMode = deviceWindowingMode }
-        topActivityInfo?.apply { packageName = runningTaskPackageName }
-      }
+    private fun createTaskInfo(
+        deviceWindowingMode: Int = WINDOWING_MODE_UNDEFINED,
+        runningTaskPackageName: String = LAUNCHER_PACKAGE_NAME,
+    ): RunningTaskInfo =
+        RunningTaskInfo().apply {
+            configuration.windowConfiguration.apply { windowingMode = deviceWindowingMode }
+            topActivityInfo?.apply { packageName = runningTaskPackageName }
+        }
 
-  private companion object {
-    const val GMAIL_PACKAGE_NAME = "com.google.android.gm"
-    const val LAUNCHER_PACKAGE_NAME = "com.google.android.apps.nexuslauncher"
-  }
+    private companion object {
+        const val GMAIL_PACKAGE_NAME = "com.google.android.gm"
+        const val LAUNCHER_PACKAGE_NAME = "com.google.android.apps.nexuslauncher"
+    }
 }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/compatui/SystemModalsTransitionHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/compatui/SystemModalsTransitionHandlerTest.kt
index c33005e..1569f9d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/compatui/SystemModalsTransitionHandlerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/compatui/SystemModalsTransitionHandlerTest.kt
@@ -25,10 +25,10 @@
 import androidx.test.filters.SmallTest
 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.desktopmode.DesktopTestHelpers.createFullscreenTask
 import com.android.wm.shell.desktopmode.DesktopTestHelpers.createFullscreenTaskBuilder
 import com.android.wm.shell.desktopmode.DesktopTestHelpers.createSystemModalTask
-import com.android.wm.shell.desktopmode.DesktopRepository
 import com.android.wm.shell.desktopmode.DesktopUserRepositories
 import com.android.wm.shell.sysui.ShellInit
 import com.android.wm.shell.transition.TransitionInfoBuilder
@@ -44,8 +44,8 @@
 import org.mockito.kotlin.whenever
 
 /**
- * Tests for {@link SystemModalsTransitionHandler}
- * Usage: atest WMShellUnitTests:SystemModalsTransitionHandlerTest
+ * Tests for {@link SystemModalsTransitionHandler} Usage: atest
+ * WMShellUnitTests:SystemModalsTransitionHandlerTest
  */
 @SmallTest
 @RunWith(AndroidTestingRunner::class)
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 9c00c0c..5475032 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
@@ -69,431 +69,448 @@
 @RunWith(AndroidTestingRunner::class)
 @OptIn(ExperimentalCoroutinesApi::class)
 class AppHandleEducationControllerTest : ShellTestCase() {
-  @JvmField
-  @Rule
-  val extendedMockitoRule =
-      ExtendedMockitoRule.Builder(this)
-          .mockStatic(DesktopModeStatus::class.java)
-          .mockStatic(SystemProperties::class.java)
-          .build()!!
-  @JvmField @Rule val setFlagsRule = SetFlagsRule()
+    @JvmField
+    @Rule
+    val extendedMockitoRule =
+        ExtendedMockitoRule.Builder(this)
+            .mockStatic(DesktopModeStatus::class.java)
+            .mockStatic(SystemProperties::class.java)
+            .build()!!
+    @JvmField @Rule val setFlagsRule = SetFlagsRule()
 
-  private lateinit var educationController: AppHandleEducationController
-  private lateinit var testableContext: TestableContext
-  private val testScope = TestScope()
-  private val testDataStoreFlow = MutableStateFlow(createWindowingEducationProto())
-  private val testCaptionStateFlow = MutableStateFlow<CaptionState>(CaptionState.NoCaption)
-  private val educationConfigCaptor =
-      argumentCaptor<DesktopWindowingEducationTooltipController.TooltipEducationViewConfig>()
-  @Mock private lateinit var mockEducationFilter: AppHandleEducationFilter
-  @Mock private lateinit var mockDataStoreRepository: AppHandleEducationDatastoreRepository
-  @Mock private lateinit var mockCaptionHandleRepository: WindowDecorCaptionHandleRepository
-  @Mock private lateinit var mockTooltipController: DesktopWindowingEducationTooltipController
+    private lateinit var educationController: AppHandleEducationController
+    private lateinit var testableContext: TestableContext
+    private val testScope = TestScope()
+    private val testDataStoreFlow = MutableStateFlow(createWindowingEducationProto())
+    private val testCaptionStateFlow = MutableStateFlow<CaptionState>(CaptionState.NoCaption)
+    private val educationConfigCaptor =
+        argumentCaptor<DesktopWindowingEducationTooltipController.TooltipEducationViewConfig>()
+    @Mock private lateinit var mockEducationFilter: AppHandleEducationFilter
+    @Mock private lateinit var mockDataStoreRepository: AppHandleEducationDatastoreRepository
+    @Mock private lateinit var mockCaptionHandleRepository: WindowDecorCaptionHandleRepository
+    @Mock private lateinit var mockTooltipController: DesktopWindowingEducationTooltipController
 
-  @Before
-  fun setUp() {
-    MockitoAnnotations.initMocks(this)
-    Dispatchers.setMain(StandardTestDispatcher(testScope.testScheduler))
-    testableContext = TestableContext(mContext)
-    whenever(mockDataStoreRepository.dataStoreFlow).thenReturn(testDataStoreFlow)
-    whenever(mockCaptionHandleRepository.captionStateFlow).thenReturn(testCaptionStateFlow)
-    whenever(DesktopModeStatus.canEnterDesktopMode(any())).thenReturn(true)
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+        Dispatchers.setMain(StandardTestDispatcher(testScope.testScheduler))
+        testableContext = TestableContext(mContext)
+        whenever(mockDataStoreRepository.dataStoreFlow).thenReturn(testDataStoreFlow)
+        whenever(mockCaptionHandleRepository.captionStateFlow).thenReturn(testCaptionStateFlow)
+        whenever(DesktopModeStatus.canEnterDesktopMode(any())).thenReturn(true)
 
-    educationController =
-        AppHandleEducationController(
-            testableContext,
-            mockEducationFilter,
-            mockDataStoreRepository,
-            mockCaptionHandleRepository,
-            mockTooltipController,
-            testScope.backgroundScope,
-            Dispatchers.Main)
-  }
+        educationController =
+            AppHandleEducationController(
+                testableContext,
+                mockEducationFilter,
+                mockDataStoreRepository,
+                mockCaptionHandleRepository,
+                mockTooltipController,
+                testScope.backgroundScope,
+                Dispatchers.Main,
+            )
+    }
 
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
-  fun init_appHandleVisible_shouldCallShowEducationTooltip() =
-      testScope.runTest {
-        // App handle is visible. Should show education tooltip.
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
+    fun init_appHandleVisible_shouldCallShowEducationTooltip() =
+        testScope.runTest {
+            // App handle is visible. Should show education tooltip.
+            setShouldShowAppHandleEducation(true)
+
+            // Simulate app handle visible.
+            testCaptionStateFlow.value = createAppHandleState()
+            // Wait for first tooltip to showup.
+            waitForBufferDelay()
+
+            verify(mockTooltipController, times(1)).showEducationTooltip(any(), any())
+        }
+
+    @Test
+    @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
+    fun init_flagDisabled_shouldNotCallShowEducationTooltip() =
+        testScope.runTest {
+            // App handle visible but education aconfig flag disabled, should not show education
+            // tooltip.
+            whenever(DesktopModeStatus.canEnterDesktopMode(any())).thenReturn(false)
+            setShouldShowAppHandleEducation(true)
+
+            // Simulate app handle visible.
+            testCaptionStateFlow.value = createAppHandleState()
+            // Wait for first tooltip to showup.
+            waitForBufferDelay()
+
+            verify(mockTooltipController, never()).showEducationTooltip(any(), any())
+        }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
+    fun init_shouldShowAppHandleEducationReturnsFalse_shouldNotCallShowEducationTooltip() =
+        testScope.runTest {
+            // App handle is visible but [shouldShowAppHandleEducation] api returns false, should
+            // not
+            // show education tooltip.
+            setShouldShowAppHandleEducation(false)
+
+            // Simulate app handle visible.
+            testCaptionStateFlow.value = createAppHandleState()
+            // Wait for first tooltip to showup.
+            waitForBufferDelay()
+
+            verify(mockTooltipController, never()).showEducationTooltip(any(), any())
+        }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
+    fun init_appHandleNotVisible_shouldNotCallShowEducationTooltip() =
+        testScope.runTest {
+            // App handle is not visible, should not show education tooltip.
+            setShouldShowAppHandleEducation(true)
+
+            // Simulate app handle is not visible.
+            testCaptionStateFlow.value = CaptionState.NoCaption
+            // Wait for first tooltip to showup.
+            waitForBufferDelay()
+
+            verify(mockTooltipController, never()).showEducationTooltip(any(), any())
+        }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
+    fun init_appHandleHintViewedAlready_shouldNotCallShowEducationTooltip() =
+        testScope.runTest {
+            // App handle is visible but app handle hint has been viewed before,
+            // should not show education tooltip.
+            // Mark app handle hint viewed.
+            testDataStoreFlow.value =
+                createWindowingEducationProto(appHandleHintViewedTimestampMillis = 123L)
+            setShouldShowAppHandleEducation(true)
+
+            // Simulate app handle visible.
+            testCaptionStateFlow.value = createAppHandleState(isHandleMenuExpanded = false)
+            // Wait for first tooltip to showup.
+            waitForBufferDelay()
+
+            verify(mockTooltipController, never()).showEducationTooltip(any(), any())
+        }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
+    fun overridePrerequisite_appHandleHintViewedAlready_shouldCallShowEducationTooltip() =
+        testScope.runTest {
+            // App handle is visible but app handle hint has been viewed before.
+            // But as we are overriding prerequisite conditions, we should show app
+            // handle tooltip.
+            // Mark app handle hint viewed.
+            testDataStoreFlow.value =
+                createWindowingEducationProto(appHandleHintViewedTimestampMillis = 123L)
+            val systemPropertiesKey =
+                "persist.desktop_windowing_app_handle_education_override_conditions"
+            whenever(SystemProperties.getBoolean(eq(systemPropertiesKey), anyBoolean()))
+                .thenReturn(true)
+            setShouldShowAppHandleEducation(true)
+
+            // Simulate app handle visible.
+            testCaptionStateFlow.value = createAppHandleState(isHandleMenuExpanded = false)
+            // Wait for first tooltip to showup.
+            waitForBufferDelay()
+
+            verify(mockTooltipController, times(1)).showEducationTooltip(any(), any())
+        }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
+    fun init_appHandleExpanded_shouldMarkAppHandleHintUsed() =
+        testScope.runTest {
+            setShouldShowAppHandleEducation(false)
+
+            // Simulate app handle visible and expanded.
+            testCaptionStateFlow.value = createAppHandleState(isHandleMenuExpanded = true)
+            // Wait for some time before verifying
+            waitForBufferDelay()
+
+            verify(mockDataStoreRepository, times(1))
+                .updateAppHandleHintUsedTimestampMillis(eq(true))
+        }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
+    fun init_showFirstTooltip_shouldMarkAppHandleHintViewed() =
+        testScope.runTest {
+            // App handle is visible. Should show education tooltip.
+            setShouldShowAppHandleEducation(true)
+
+            // Simulate app handle visible.
+            testCaptionStateFlow.value = createAppHandleState()
+            // Wait for first tooltip to showup.
+            waitForBufferDelay()
+
+            verify(mockDataStoreRepository, times(1))
+                .updateAppHandleHintViewedTimestampMillis(eq(true))
+        }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
+    @Ignore("b/371527084: revisit testcase after refactoring original logic")
+    fun showWindowingImageButtonTooltip_appHandleExpanded_shouldCallShowEducationTooltipTwice() =
+        testScope.runTest {
+            // After first tooltip is dismissed, app handle is expanded. Should show second
+            // education
+            // tooltip.
+            showAndDismissFirstTooltip()
+
+            // Simulate app handle expanded.
+            testCaptionStateFlow.value = createAppHandleState(isHandleMenuExpanded = true)
+            // Wait for next tooltip to showup.
+            waitForBufferDelay()
+
+            // [showEducationTooltip] should be called twice, once for each tooltip.
+            verify(mockTooltipController, times(2)).showEducationTooltip(any(), any())
+        }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
+    @Ignore("b/371527084: revisit testcase after refactoring original logic")
+    fun showWindowingImageButtonTooltip_appHandleExpandedAfterTimeout_shouldCallShowEducationTooltipOnce() =
+        testScope.runTest {
+            // After first tooltip is dismissed, app handle is expanded after timeout. Should not
+            // show
+            // second education tooltip.
+            showAndDismissFirstTooltip()
+
+            // Wait for timeout to occur, after this timeout we should not listen for further
+            // triggers
+            // anymore.
+            advanceTimeBy(APP_HANDLE_EDUCATION_TIMEOUT_BUFFER_MILLIS)
+            runCurrent()
+            // Simulate app handle expanded.
+            testCaptionStateFlow.value = createAppHandleState(isHandleMenuExpanded = true)
+            // Wait for next tooltip to showup.
+            waitForBufferDelay()
+
+            // [showEducationTooltip] should be called once, just for the first tooltip.
+            verify(mockTooltipController, times(1)).showEducationTooltip(any(), any())
+        }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
+    @Ignore("b/371527084: revisit testcase after refactoring original logic")
+    fun showWindowingImageButtonTooltip_appHandleExpandedTwice_shouldCallShowEducationTooltipTwice() =
+        testScope.runTest {
+            // After first tooltip is dismissed, app handle is expanded twice. Should show second
+            // education tooltip only once.
+            showAndDismissFirstTooltip()
+
+            // Simulate app handle expanded.
+            testCaptionStateFlow.value = createAppHandleState(isHandleMenuExpanded = true)
+            // Wait for next tooltip to showup.
+            waitForBufferDelay()
+            // Simulate app handle being expanded twice.
+            testCaptionStateFlow.value = createAppHandleState(isHandleMenuExpanded = true)
+            waitForBufferDelay()
+
+            // [showEducationTooltip] should not be called thrice, even if app handle was expanded
+            // twice. Should be called twice, once for each tooltip.
+            verify(mockTooltipController, times(2)).showEducationTooltip(any(), any())
+        }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
+    @Ignore("b/371527084: revisit testcase after refactoring original logic")
+    fun showWindowingImageButtonTooltip_appHandleNotExpanded_shouldCallShowEducationTooltipOnce() =
+        testScope.runTest {
+            // After first tooltip is dismissed, app handle is not expanded. Should not show second
+            // education tooltip.
+            showAndDismissFirstTooltip()
+
+            // Simulate app handle visible but not expanded.
+            testCaptionStateFlow.value = createAppHandleState(isHandleMenuExpanded = false)
+            // Wait for next tooltip to showup.
+            waitForBufferDelay()
+
+            // [showEducationTooltip] should be called once, just for the first tooltip.
+            verify(mockTooltipController, times(1)).showEducationTooltip(any(), any())
+        }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
+    @Ignore("b/371527084: revisit testcase after refactoring original logic")
+    fun showExitWindowingButtonTooltip_appHeaderVisible_shouldCallShowEducationTooltipThrice() =
+        testScope.runTest {
+            // After first two tooltips are dismissed, app header is visible. Should show third
+            // education tooltip.
+            showAndDismissFirstTooltip()
+            showAndDismissSecondTooltip()
+
+            // Simulate app header visible.
+            testCaptionStateFlow.value = createAppHeaderState()
+            // Wait for next tooltip to showup.
+            waitForBufferDelay()
+
+            verify(mockTooltipController, times(3)).showEducationTooltip(any(), any())
+        }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
+    @Ignore("b/371527084: revisit testcase after refactoring original logic")
+    fun showExitWindowingButtonTooltip_appHeaderVisibleAfterTimeout_shouldCallShowEducationTooltipTwice() =
+        testScope.runTest {
+            // After first two tooltips are dismissed, app header is visible after timeout. Should
+            // not
+            // show third education tooltip.
+            showAndDismissFirstTooltip()
+            showAndDismissSecondTooltip()
+
+            // Wait for timeout to occur, after this timeout we should not listen for further
+            // triggers
+            // anymore.
+            advanceTimeBy(APP_HANDLE_EDUCATION_TIMEOUT_BUFFER_MILLIS)
+            runCurrent()
+            // Simulate app header visible.
+            testCaptionStateFlow.value = createAppHeaderState()
+            // Wait for next tooltip to showup.
+            waitForBufferDelay()
+
+            verify(mockTooltipController, times(2)).showEducationTooltip(any(), any())
+        }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
+    @Ignore("b/371527084: revisit testcase after refactoring original logic")
+    fun showExitWindowingButtonTooltip_appHeaderVisibleTwice_shouldCallShowEducationTooltipThrice() =
+        testScope.runTest {
+            // After first two tooltips are dismissed, app header is visible twice. Should show
+            // third
+            // education tooltip only once.
+            showAndDismissFirstTooltip()
+            showAndDismissSecondTooltip()
+
+            // Simulate app header visible.
+            testCaptionStateFlow.value = createAppHeaderState()
+            // Wait for next tooltip to showup.
+            waitForBufferDelay()
+            testCaptionStateFlow.value = createAppHeaderState()
+            // Wait for next tooltip to showup.
+            waitForBufferDelay()
+
+            verify(mockTooltipController, times(3)).showEducationTooltip(any(), any())
+        }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
+    @Ignore("b/371527084: revisit testcase after refactoring original logic")
+    fun showExitWindowingButtonTooltip_appHeaderExpanded_shouldCallShowEducationTooltipTwice() =
+        testScope.runTest {
+            // After first two tooltips are dismissed, app header is visible but expanded. Should
+            // not
+            // show third education tooltip.
+            showAndDismissFirstTooltip()
+            showAndDismissSecondTooltip()
+
+            // Simulate app header visible.
+            testCaptionStateFlow.value = createAppHeaderState(isHeaderMenuExpanded = true)
+            // Wait for next tooltip to showup.
+            waitForBufferDelay()
+
+            verify(mockTooltipController, times(2)).showEducationTooltip(any(), any())
+        }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
+    fun setAppHandleEducationTooltipCallbacks_onAppHandleTooltipClicked_callbackInvoked() =
+        testScope.runTest {
+            // App handle is visible. Should show education tooltip.
+            setShouldShowAppHandleEducation(true)
+            val mockOpenHandleMenuCallback: (Int) -> Unit = mock()
+            val mockToDesktopModeCallback: (Int, DesktopModeTransitionSource) -> Unit = mock()
+            educationController.setAppHandleEducationTooltipCallbacks(
+                mockOpenHandleMenuCallback,
+                mockToDesktopModeCallback,
+            )
+            // Simulate app handle visible.
+            testCaptionStateFlow.value = createAppHandleState()
+            // Wait for first tooltip to showup.
+            waitForBufferDelay()
+
+            verify(mockTooltipController, atLeastOnce())
+                .showEducationTooltip(educationConfigCaptor.capture(), any())
+            educationConfigCaptor.lastValue.onEducationClickAction.invoke()
+
+            verify(mockOpenHandleMenuCallback, times(1)).invoke(any())
+        }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
+    @Ignore("b/371527084: revisit testcase after refactoring original logic")
+    fun setAppHandleEducationTooltipCallbacks_onWindowingImageButtonTooltipClicked_callbackInvoked() =
+        testScope.runTest {
+            // After first tooltip is dismissed, app handle is expanded. Should show second
+            // education
+            // tooltip.
+            showAndDismissFirstTooltip()
+            val mockOpenHandleMenuCallback: (Int) -> Unit = mock()
+            val mockToDesktopModeCallback: (Int, DesktopModeTransitionSource) -> Unit = mock()
+            educationController.setAppHandleEducationTooltipCallbacks(
+                mockOpenHandleMenuCallback,
+                mockToDesktopModeCallback,
+            )
+            // Simulate app handle expanded.
+            testCaptionStateFlow.value = createAppHandleState(isHandleMenuExpanded = true)
+            // Wait for next tooltip to showup.
+            waitForBufferDelay()
+
+            verify(mockTooltipController, atLeastOnce())
+                .showEducationTooltip(educationConfigCaptor.capture(), any())
+            educationConfigCaptor.lastValue.onEducationClickAction.invoke()
+
+            verify(mockToDesktopModeCallback, times(1)).invoke(any(), any())
+        }
+
+    private suspend fun TestScope.showAndDismissFirstTooltip() {
         setShouldShowAppHandleEducation(true)
-
         // Simulate app handle visible.
-        testCaptionStateFlow.value = createAppHandleState()
+        testCaptionStateFlow.value = createAppHandleState(isHandleMenuExpanded = false)
         // Wait for first tooltip to showup.
         waitForBufferDelay()
-
-        verify(mockTooltipController, times(1)).showEducationTooltip(any(), any())
-      }
-
-  @Test
-  @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
-  fun init_flagDisabled_shouldNotCallShowEducationTooltip() =
-      testScope.runTest {
-        // App handle visible but education aconfig flag disabled, should not show education
-        // tooltip.
-        whenever(DesktopModeStatus.canEnterDesktopMode(any())).thenReturn(false)
-        setShouldShowAppHandleEducation(true)
-
-        // Simulate app handle visible.
-        testCaptionStateFlow.value = createAppHandleState()
-        // Wait for first tooltip to showup.
-        waitForBufferDelay()
-
-        verify(mockTooltipController, never()).showEducationTooltip(any(), any())
-      }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
-  fun init_shouldShowAppHandleEducationReturnsFalse_shouldNotCallShowEducationTooltip() =
-      testScope.runTest {
-        // App handle is visible but [shouldShowAppHandleEducation] api returns false, should not
-        // show education tooltip.
+        // [shouldShowAppHandleEducation] should return false as education has been viewed
+        // before.
         setShouldShowAppHandleEducation(false)
+        // Dismiss previous tooltip, after this we should listen for next tooltip's trigger.
+        captureAndInvokeOnDismissAction()
+    }
 
-        // Simulate app handle visible.
-        testCaptionStateFlow.value = createAppHandleState()
-        // Wait for first tooltip to showup.
-        waitForBufferDelay()
-
-        verify(mockTooltipController, never()).showEducationTooltip(any(), any())
-      }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
-  fun init_appHandleNotVisible_shouldNotCallShowEducationTooltip() =
-      testScope.runTest {
-        // App handle is not visible, should not show education tooltip.
-        setShouldShowAppHandleEducation(true)
-
-        // Simulate app handle is not visible.
-        testCaptionStateFlow.value = CaptionState.NoCaption
-        // Wait for first tooltip to showup.
-        waitForBufferDelay()
-
-        verify(mockTooltipController, never()).showEducationTooltip(any(), any())
-      }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
-  fun init_appHandleHintViewedAlready_shouldNotCallShowEducationTooltip() =
-      testScope.runTest {
-        // App handle is visible but app handle hint has been viewed before,
-        // should not show education tooltip.
-        // Mark app handle hint viewed.
-        testDataStoreFlow.value =
-            createWindowingEducationProto(appHandleHintViewedTimestampMillis = 123L)
-        setShouldShowAppHandleEducation(true)
-
-        // Simulate app handle visible.
-        testCaptionStateFlow.value = createAppHandleState(isHandleMenuExpanded = false)
-        // Wait for first tooltip to showup.
-        waitForBufferDelay()
-
-        verify(mockTooltipController, never()).showEducationTooltip(any(), any())
-      }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
-  fun overridePrerequisite_appHandleHintViewedAlready_shouldCallShowEducationTooltip() =
-      testScope.runTest {
-        // App handle is visible but app handle hint has been viewed before.
-        // But as we are overriding prerequisite conditions, we should show app
-        // handle tooltip.
-        // Mark app handle hint viewed.
-        testDataStoreFlow.value =
-            createWindowingEducationProto(appHandleHintViewedTimestampMillis = 123L)
-        val systemPropertiesKey =
-            "persist.desktop_windowing_app_handle_education_override_conditions"
-        whenever(SystemProperties.getBoolean(eq(systemPropertiesKey), anyBoolean()))
-            .thenReturn(true)
-        setShouldShowAppHandleEducation(true)
-
-        // Simulate app handle visible.
-        testCaptionStateFlow.value = createAppHandleState(isHandleMenuExpanded = false)
-        // Wait for first tooltip to showup.
-        waitForBufferDelay()
-
-        verify(mockTooltipController, times(1)).showEducationTooltip(any(), any())
-      }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
-  fun init_appHandleExpanded_shouldMarkAppHandleHintUsed() =
-      testScope.runTest {
-        setShouldShowAppHandleEducation(false)
-
-        // Simulate app handle visible and expanded.
-        testCaptionStateFlow.value = createAppHandleState(isHandleMenuExpanded = true)
-        // Wait for some time before verifying
-        waitForBufferDelay()
-
-        verify(mockDataStoreRepository, times(1)).updateAppHandleHintUsedTimestampMillis(eq(true))
-      }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
-  fun init_showFirstTooltip_shouldMarkAppHandleHintViewed() =
-      testScope.runTest {
-        // App handle is visible. Should show education tooltip.
-        setShouldShowAppHandleEducation(true)
-
-        // Simulate app handle visible.
-        testCaptionStateFlow.value = createAppHandleState()
-        // Wait for first tooltip to showup.
-        waitForBufferDelay()
-
-        verify(mockDataStoreRepository, times(1)).updateAppHandleHintViewedTimestampMillis(eq(true))
-      }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
-  @Ignore("b/371527084: revisit testcase after refactoring original logic")
-  fun showWindowingImageButtonTooltip_appHandleExpanded_shouldCallShowEducationTooltipTwice() =
-      testScope.runTest {
-        // After first tooltip is dismissed, app handle is expanded. Should show second education
-        // tooltip.
-        showAndDismissFirstTooltip()
-
+    private fun TestScope.showAndDismissSecondTooltip() {
         // Simulate app handle expanded.
         testCaptionStateFlow.value = createAppHandleState(isHandleMenuExpanded = true)
         // Wait for next tooltip to showup.
         waitForBufferDelay()
+        // Dismiss previous tooltip, after this we should listen for next tooltip's trigger.
+        captureAndInvokeOnDismissAction()
+    }
 
-        // [showEducationTooltip] should be called twice, once for each tooltip.
-        verify(mockTooltipController, times(2)).showEducationTooltip(any(), any())
-      }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
-  @Ignore("b/371527084: revisit testcase after refactoring original logic")
-  fun showWindowingImageButtonTooltip_appHandleExpandedAfterTimeout_shouldCallShowEducationTooltipOnce() =
-      testScope.runTest {
-        // After first tooltip is dismissed, app handle is expanded after timeout. Should not show
-        // second education tooltip.
-        showAndDismissFirstTooltip()
-
-        // Wait for timeout to occur, after this timeout we should not listen for further triggers
-        // anymore.
-        advanceTimeBy(APP_HANDLE_EDUCATION_TIMEOUT_BUFFER_MILLIS)
-        runCurrent()
-        // Simulate app handle expanded.
-        testCaptionStateFlow.value = createAppHandleState(isHandleMenuExpanded = true)
-        // Wait for next tooltip to showup.
-        waitForBufferDelay()
-
-        // [showEducationTooltip] should be called once, just for the first tooltip.
-        verify(mockTooltipController, times(1)).showEducationTooltip(any(), any())
-      }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
-  @Ignore("b/371527084: revisit testcase after refactoring original logic")
-  fun showWindowingImageButtonTooltip_appHandleExpandedTwice_shouldCallShowEducationTooltipTwice() =
-      testScope.runTest {
-        // After first tooltip is dismissed, app handle is expanded twice. Should show second
-        // education tooltip only once.
-        showAndDismissFirstTooltip()
-
-        // Simulate app handle expanded.
-        testCaptionStateFlow.value = createAppHandleState(isHandleMenuExpanded = true)
-        // Wait for next tooltip to showup.
-        waitForBufferDelay()
-        // Simulate app handle being expanded twice.
-        testCaptionStateFlow.value = createAppHandleState(isHandleMenuExpanded = true)
-        waitForBufferDelay()
-
-        // [showEducationTooltip] should not be called thrice, even if app handle was expanded
-        // twice. Should be called twice, once for each tooltip.
-        verify(mockTooltipController, times(2)).showEducationTooltip(any(), any())
-      }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
-  @Ignore("b/371527084: revisit testcase after refactoring original logic")
-  fun showWindowingImageButtonTooltip_appHandleNotExpanded_shouldCallShowEducationTooltipOnce() =
-      testScope.runTest {
-        // After first tooltip is dismissed, app handle is not expanded. Should not show second
-        // education tooltip.
-        showAndDismissFirstTooltip()
-
-        // Simulate app handle visible but not expanded.
-        testCaptionStateFlow.value = createAppHandleState(isHandleMenuExpanded = false)
-        // Wait for next tooltip to showup.
-        waitForBufferDelay()
-
-        // [showEducationTooltip] should be called once, just for the first tooltip.
-        verify(mockTooltipController, times(1)).showEducationTooltip(any(), any())
-      }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
-  @Ignore("b/371527084: revisit testcase after refactoring original logic")
-  fun showExitWindowingButtonTooltip_appHeaderVisible_shouldCallShowEducationTooltipThrice() =
-      testScope.runTest {
-        // After first two tooltips are dismissed, app header is visible. Should show third
-        // education tooltip.
-        showAndDismissFirstTooltip()
-        showAndDismissSecondTooltip()
-
-        // Simulate app header visible.
-        testCaptionStateFlow.value = createAppHeaderState()
-        // Wait for next tooltip to showup.
-        waitForBufferDelay()
-
-        verify(mockTooltipController, times(3)).showEducationTooltip(any(), any())
-      }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
-  @Ignore("b/371527084: revisit testcase after refactoring original logic")
-  fun showExitWindowingButtonTooltip_appHeaderVisibleAfterTimeout_shouldCallShowEducationTooltipTwice() =
-      testScope.runTest {
-        // After first two tooltips are dismissed, app header is visible after timeout. Should not
-        // show third education tooltip.
-        showAndDismissFirstTooltip()
-        showAndDismissSecondTooltip()
-
-        // Wait for timeout to occur, after this timeout we should not listen for further triggers
-        // anymore.
-        advanceTimeBy(APP_HANDLE_EDUCATION_TIMEOUT_BUFFER_MILLIS)
-        runCurrent()
-        // Simulate app header visible.
-        testCaptionStateFlow.value = createAppHeaderState()
-        // Wait for next tooltip to showup.
-        waitForBufferDelay()
-
-        verify(mockTooltipController, times(2)).showEducationTooltip(any(), any())
-      }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
-  @Ignore("b/371527084: revisit testcase after refactoring original logic")
-  fun showExitWindowingButtonTooltip_appHeaderVisibleTwice_shouldCallShowEducationTooltipThrice() =
-      testScope.runTest {
-        // After first two tooltips are dismissed, app header is visible twice. Should show third
-        // education tooltip only once.
-        showAndDismissFirstTooltip()
-        showAndDismissSecondTooltip()
-
-        // Simulate app header visible.
-        testCaptionStateFlow.value = createAppHeaderState()
-        // Wait for next tooltip to showup.
-        waitForBufferDelay()
-        testCaptionStateFlow.value = createAppHeaderState()
-        // Wait for next tooltip to showup.
-        waitForBufferDelay()
-
-        verify(mockTooltipController, times(3)).showEducationTooltip(any(), any())
-      }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
-  @Ignore("b/371527084: revisit testcase after refactoring original logic")
-  fun showExitWindowingButtonTooltip_appHeaderExpanded_shouldCallShowEducationTooltipTwice() =
-      testScope.runTest {
-        // After first two tooltips are dismissed, app header is visible but expanded. Should not
-        // show third education tooltip.
-        showAndDismissFirstTooltip()
-        showAndDismissSecondTooltip()
-
-        // Simulate app header visible.
-        testCaptionStateFlow.value = createAppHeaderState(isHeaderMenuExpanded = true)
-        // Wait for next tooltip to showup.
-        waitForBufferDelay()
-
-        verify(mockTooltipController, times(2)).showEducationTooltip(any(), any())
-      }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
-  fun setAppHandleEducationTooltipCallbacks_onAppHandleTooltipClicked_callbackInvoked() =
-      testScope.runTest {
-        // App handle is visible. Should show education tooltip.
-        setShouldShowAppHandleEducation(true)
-        val mockOpenHandleMenuCallback: (Int) -> Unit = mock()
-        val mockToDesktopModeCallback: (Int, DesktopModeTransitionSource) -> Unit = mock()
-        educationController.setAppHandleEducationTooltipCallbacks(
-            mockOpenHandleMenuCallback, mockToDesktopModeCallback)
-        // Simulate app handle visible.
-        testCaptionStateFlow.value = createAppHandleState()
-        // Wait for first tooltip to showup.
-        waitForBufferDelay()
-
+    private fun captureAndInvokeOnDismissAction() {
         verify(mockTooltipController, atLeastOnce())
             .showEducationTooltip(educationConfigCaptor.capture(), any())
-        educationConfigCaptor.lastValue.onEducationClickAction.invoke()
+        educationConfigCaptor.lastValue.onDismissAction.invoke()
+    }
 
-        verify(mockOpenHandleMenuCallback, times(1)).invoke(any())
-      }
+    private suspend fun setShouldShowAppHandleEducation(shouldShowAppHandleEducation: Boolean) =
+        whenever(mockEducationFilter.shouldShowAppHandleEducation(any()))
+            .thenReturn(shouldShowAppHandleEducation)
 
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
-  @Ignore("b/371527084: revisit testcase after refactoring original logic")
-  fun setAppHandleEducationTooltipCallbacks_onWindowingImageButtonTooltipClicked_callbackInvoked() =
-      testScope.runTest {
-        // After first tooltip is dismissed, app handle is expanded. Should show second education
-        // tooltip.
-        showAndDismissFirstTooltip()
-        val mockOpenHandleMenuCallback: (Int) -> Unit = mock()
-        val mockToDesktopModeCallback: (Int, DesktopModeTransitionSource) -> Unit = mock()
-        educationController.setAppHandleEducationTooltipCallbacks(
-            mockOpenHandleMenuCallback, mockToDesktopModeCallback)
-        // Simulate app handle expanded.
-        testCaptionStateFlow.value = createAppHandleState(isHandleMenuExpanded = true)
-        // Wait for next tooltip to showup.
-        waitForBufferDelay()
+    /**
+     * Class under test waits for some time before showing education, simulate advance time before
+     * verifying or moving forward
+     */
+    private fun TestScope.waitForBufferDelay() {
+        advanceTimeBy(APP_HANDLE_EDUCATION_DELAY_BUFFER_MILLIS)
+        runCurrent()
+    }
 
-        verify(mockTooltipController, atLeastOnce())
-            .showEducationTooltip(educationConfigCaptor.capture(), any())
-        educationConfigCaptor.lastValue.onEducationClickAction.invoke()
-
-        verify(mockToDesktopModeCallback, times(1)).invoke(any(), any())
-      }
-
-  private suspend fun TestScope.showAndDismissFirstTooltip() {
-    setShouldShowAppHandleEducation(true)
-    // Simulate app handle visible.
-    testCaptionStateFlow.value = createAppHandleState(isHandleMenuExpanded = false)
-    // Wait for first tooltip to showup.
-    waitForBufferDelay()
-    // [shouldShowAppHandleEducation] should return false as education has been viewed
-    // before.
-    setShouldShowAppHandleEducation(false)
-    // Dismiss previous tooltip, after this we should listen for next tooltip's trigger.
-    captureAndInvokeOnDismissAction()
-  }
-
-  private fun TestScope.showAndDismissSecondTooltip() {
-    // Simulate app handle expanded.
-    testCaptionStateFlow.value = createAppHandleState(isHandleMenuExpanded = true)
-    // Wait for next tooltip to showup.
-    waitForBufferDelay()
-    // Dismiss previous tooltip, after this we should listen for next tooltip's trigger.
-    captureAndInvokeOnDismissAction()
-  }
-
-  private fun captureAndInvokeOnDismissAction() {
-    verify(mockTooltipController, atLeastOnce())
-        .showEducationTooltip(educationConfigCaptor.capture(), any())
-    educationConfigCaptor.lastValue.onDismissAction.invoke()
-  }
-
-  private suspend fun setShouldShowAppHandleEducation(shouldShowAppHandleEducation: Boolean) =
-      whenever(mockEducationFilter.shouldShowAppHandleEducation(any()))
-          .thenReturn(shouldShowAppHandleEducation)
-
-  /**
-   * Class under test waits for some time before showing education, simulate advance time before
-   * verifying or moving forward
-   */
-  private fun TestScope.waitForBufferDelay() {
-    advanceTimeBy(APP_HANDLE_EDUCATION_DELAY_BUFFER_MILLIS)
-    runCurrent()
-  }
-
-  private companion object {
-    val APP_HANDLE_EDUCATION_DELAY_BUFFER_MILLIS: Long = APP_HANDLE_EDUCATION_DELAY_MILLIS + 1000L
-    val APP_HANDLE_EDUCATION_TIMEOUT_BUFFER_MILLIS: Long =
-        APP_HANDLE_EDUCATION_TIMEOUT_MILLIS + 1000L
-  }
+    private companion object {
+        val APP_HANDLE_EDUCATION_DELAY_BUFFER_MILLIS: Long =
+            APP_HANDLE_EDUCATION_DELAY_MILLIS + 1000L
+        val APP_HANDLE_EDUCATION_TIMEOUT_BUFFER_MILLIS: Long =
+            APP_HANDLE_EDUCATION_TIMEOUT_MILLIS + 1000L
+    }
 }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationDatastoreRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationDatastoreRepositoryTest.kt
index 963890d..4db883d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationDatastoreRepositoryTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationDatastoreRepositoryTest.kt
@@ -49,85 +49,89 @@
 @RunWith(AndroidTestingRunner::class)
 @ExperimentalCoroutinesApi
 class AppHandleEducationDatastoreRepositoryTest {
-  private val testContext: Context = InstrumentationRegistry.getInstrumentation().targetContext
-  private lateinit var testDatastore: DataStore<WindowingEducationProto>
-  private lateinit var datastoreRepository: AppHandleEducationDatastoreRepository
-  private lateinit var datastoreScope: CoroutineScope
+    private val testContext: Context = InstrumentationRegistry.getInstrumentation().targetContext
+    private lateinit var testDatastore: DataStore<WindowingEducationProto>
+    private lateinit var datastoreRepository: AppHandleEducationDatastoreRepository
+    private lateinit var datastoreScope: CoroutineScope
 
-  @Before
-  fun setUp() {
-    Dispatchers.setMain(StandardTestDispatcher())
-    datastoreScope = CoroutineScope(Dispatchers.Unconfined + SupervisorJob())
-    testDatastore =
-        DataStoreFactory.create(
-            serializer =
-                AppHandleEducationDatastoreRepository.Companion.WindowingEducationProtoSerializer,
-            scope = datastoreScope) {
-              testContext.dataStoreFile(APP_HANDLE_EDUCATION_DATASTORE_TEST_FILE)
+    @Before
+    fun setUp() {
+        Dispatchers.setMain(StandardTestDispatcher())
+        datastoreScope = CoroutineScope(Dispatchers.Unconfined + SupervisorJob())
+        testDatastore =
+            DataStoreFactory.create(
+                serializer =
+                    AppHandleEducationDatastoreRepository.Companion
+                        .WindowingEducationProtoSerializer,
+                scope = datastoreScope,
+            ) {
+                testContext.dataStoreFile(APP_HANDLE_EDUCATION_DATASTORE_TEST_FILE)
             }
-    datastoreRepository = AppHandleEducationDatastoreRepository(testDatastore)
-  }
+        datastoreRepository = AppHandleEducationDatastoreRepository(testDatastore)
+    }
 
-  @After
-  fun tearDown() {
-    File(ApplicationProvider.getApplicationContext<Context>().filesDir, "datastore")
-        .deleteRecursively()
+    @After
+    fun tearDown() {
+        File(ApplicationProvider.getApplicationContext<Context>().filesDir, "datastore")
+            .deleteRecursively()
 
-    datastoreScope.cancel()
-  }
+        datastoreScope.cancel()
+    }
 
-  @Test
-  fun getWindowingEducationProto_returnsCorrectProto() =
-      runTest(StandardTestDispatcher()) {
-        val windowingEducationProto =
-            createWindowingEducationProto(
-                appHandleHintViewedTimestampMillis = 123L,
-                appHandleHintUsedTimestampMillis = 124L,
-                appUsageStats = mapOf(GMAIL_PACKAGE_NAME to 2),
-                appUsageStatsLastUpdateTimestampMillis = 125L)
-        testDatastore.updateData { windowingEducationProto }
+    @Test
+    fun getWindowingEducationProto_returnsCorrectProto() =
+        runTest(StandardTestDispatcher()) {
+            val windowingEducationProto =
+                createWindowingEducationProto(
+                    appHandleHintViewedTimestampMillis = 123L,
+                    appHandleHintUsedTimestampMillis = 124L,
+                    appUsageStats = mapOf(GMAIL_PACKAGE_NAME to 2),
+                    appUsageStatsLastUpdateTimestampMillis = 125L,
+                )
+            testDatastore.updateData { windowingEducationProto }
 
-        val resultProto = datastoreRepository.windowingEducationProto()
+            val resultProto = datastoreRepository.windowingEducationProto()
 
-        assertThat(resultProto).isEqualTo(windowingEducationProto)
-      }
+            assertThat(resultProto).isEqualTo(windowingEducationProto)
+        }
 
-  @Test
-  fun updateAppUsageStats_updatesDatastoreProto() =
-      runTest(StandardTestDispatcher()) {
-        val appUsageStats = mapOf(GMAIL_PACKAGE_NAME to 3)
-        val appUsageStatsLastUpdateTimestamp = Duration.ofMillis(123L)
-        val windowingEducationProto =
-            createWindowingEducationProto(
-                appUsageStats = appUsageStats,
-                appUsageStatsLastUpdateTimestampMillis =
-                    appUsageStatsLastUpdateTimestamp.toMillis())
+    @Test
+    fun updateAppUsageStats_updatesDatastoreProto() =
+        runTest(StandardTestDispatcher()) {
+            val appUsageStats = mapOf(GMAIL_PACKAGE_NAME to 3)
+            val appUsageStatsLastUpdateTimestamp = Duration.ofMillis(123L)
+            val windowingEducationProto =
+                createWindowingEducationProto(
+                    appUsageStats = appUsageStats,
+                    appUsageStatsLastUpdateTimestampMillis =
+                        appUsageStatsLastUpdateTimestamp.toMillis(),
+                )
 
-        datastoreRepository.updateAppUsageStats(appUsageStats, appUsageStatsLastUpdateTimestamp)
+            datastoreRepository.updateAppUsageStats(appUsageStats, appUsageStatsLastUpdateTimestamp)
 
-        val result = testDatastore.data.first()
-        assertThat(result).isEqualTo(windowingEducationProto)
-      }
+            val result = testDatastore.data.first()
+            assertThat(result).isEqualTo(windowingEducationProto)
+        }
 
-  @Test
-  fun updateAppHandleHintViewedTimestampMillis_updatesDatastoreProto() =
-      runTest(StandardTestDispatcher()) {
-        datastoreRepository.updateAppHandleHintViewedTimestampMillis(true)
+    @Test
+    fun updateAppHandleHintViewedTimestampMillis_updatesDatastoreProto() =
+        runTest(StandardTestDispatcher()) {
+            datastoreRepository.updateAppHandleHintViewedTimestampMillis(true)
 
-        val result = testDatastore.data.first().hasAppHandleHintViewedTimestampMillis()
-        assertThat(result).isEqualTo(true)
-      }
+            val result = testDatastore.data.first().hasAppHandleHintViewedTimestampMillis()
+            assertThat(result).isEqualTo(true)
+        }
 
-  @Test
-  fun updateAppHandleHintUsedTimestampMillis_updatesDatastoreProto() =
-      runTest(StandardTestDispatcher()) {
-        datastoreRepository.updateAppHandleHintUsedTimestampMillis(true)
+    @Test
+    fun updateAppHandleHintUsedTimestampMillis_updatesDatastoreProto() =
+        runTest(StandardTestDispatcher()) {
+            datastoreRepository.updateAppHandleHintUsedTimestampMillis(true)
 
-        val result = testDatastore.data.first().hasAppHandleHintUsedTimestampMillis()
-        assertThat(result).isEqualTo(true)
-      }
+            val result = testDatastore.data.first().hasAppHandleHintUsedTimestampMillis()
+            assertThat(result).isEqualTo(true)
+        }
 
-  companion object {
-    private const val APP_HANDLE_EDUCATION_DATASTORE_TEST_FILE = "app_handle_education_test.pb"
-  }
+    companion object {
+        private const val APP_HANDLE_EDUCATION_DATASTORE_TEST_FILE = "app_handle_education_test.pb"
+    }
 }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationFilterTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationFilterTest.kt
index e5edd69..2fc36ef 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationFilterTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationFilterTest.kt
@@ -53,189 +53,221 @@
 @RunWith(AndroidTestingRunner::class)
 @kotlinx.coroutines.ExperimentalCoroutinesApi
 class AppHandleEducationFilterTest : ShellTestCase() {
-  @JvmField
-  @Rule
-  val extendedMockitoRule =
-      ExtendedMockitoRule.Builder(this).mockStatic(SystemProperties::class.java).build()!!
-  @Mock private lateinit var datastoreRepository: AppHandleEducationDatastoreRepository
-  @Mock private lateinit var mockUsageStatsManager: UsageStatsManager
-  private lateinit var educationFilter: AppHandleEducationFilter
-  private lateinit var testableResources: TestableResources
-  private lateinit var testableContext: TestableContext
+    @JvmField
+    @Rule
+    val extendedMockitoRule =
+        ExtendedMockitoRule.Builder(this).mockStatic(SystemProperties::class.java).build()!!
+    @Mock private lateinit var datastoreRepository: AppHandleEducationDatastoreRepository
+    @Mock private lateinit var mockUsageStatsManager: UsageStatsManager
+    private lateinit var educationFilter: AppHandleEducationFilter
+    private lateinit var testableResources: TestableResources
+    private lateinit var testableContext: TestableContext
 
-  @Before
-  fun setup() {
-    MockitoAnnotations.initMocks(this)
-    testableContext = TestableContext(mContext)
-    testableResources =
-        testableContext.orCreateTestableResources.apply {
-          addOverride(
-              R.array.desktop_windowing_app_handle_education_allowlist_apps,
-              arrayOf(GMAIL_PACKAGE_NAME))
-          addOverride(R.integer.desktop_windowing_education_required_time_since_setup_seconds, 0)
-          addOverride(R.integer.desktop_windowing_education_min_app_launch_count, 3)
-          addOverride(
-              R.integer.desktop_windowing_education_app_usage_cache_interval_seconds, MAX_VALUE)
-          addOverride(R.integer.desktop_windowing_education_app_launch_interval_seconds, 100)
-        }
-    testableContext.addMockSystemService(Context.USAGE_STATS_SERVICE, mockUsageStatsManager)
-    educationFilter = AppHandleEducationFilter(testableContext, datastoreRepository)
-  }
+    @Before
+    fun setup() {
+        MockitoAnnotations.initMocks(this)
+        testableContext = TestableContext(mContext)
+        testableResources =
+            testableContext.orCreateTestableResources.apply {
+                addOverride(
+                    R.array.desktop_windowing_app_handle_education_allowlist_apps,
+                    arrayOf(GMAIL_PACKAGE_NAME),
+                )
+                addOverride(
+                    R.integer.desktop_windowing_education_required_time_since_setup_seconds,
+                    0,
+                )
+                addOverride(R.integer.desktop_windowing_education_min_app_launch_count, 3)
+                addOverride(
+                    R.integer.desktop_windowing_education_app_usage_cache_interval_seconds,
+                    MAX_VALUE,
+                )
+                addOverride(R.integer.desktop_windowing_education_app_launch_interval_seconds, 100)
+            }
+        testableContext.addMockSystemService(Context.USAGE_STATS_SERVICE, mockUsageStatsManager)
+        educationFilter = AppHandleEducationFilter(testableContext, datastoreRepository)
+    }
 
-  @Test
-  fun shouldShowAppHandleEducation_isTriggerValid_returnsTrue() = runTest {
-    // setup() makes sure that all of the conditions satisfy and #shouldShowAppHandleEducation
-    // should return true
-    val windowingEducationProto =
-        createWindowingEducationProto(
-            appUsageStats = mapOf(GMAIL_PACKAGE_NAME to 4),
-            appUsageStatsLastUpdateTimestampMillis = Long.MAX_VALUE)
-    `when`(datastoreRepository.windowingEducationProto()).thenReturn(windowingEducationProto)
+    @Test
+    fun shouldShowAppHandleEducation_isTriggerValid_returnsTrue() = runTest {
+        // setup() makes sure that all of the conditions satisfy and #shouldShowAppHandleEducation
+        // should return true
+        val windowingEducationProto =
+            createWindowingEducationProto(
+                appUsageStats = mapOf(GMAIL_PACKAGE_NAME to 4),
+                appUsageStatsLastUpdateTimestampMillis = Long.MAX_VALUE,
+            )
+        `when`(datastoreRepository.windowingEducationProto()).thenReturn(windowingEducationProto)
 
-    val result = educationFilter.shouldShowAppHandleEducation(createAppHandleState())
+        val result = educationFilter.shouldShowAppHandleEducation(createAppHandleState())
 
-    assertThat(result).isTrue()
-  }
+        assertThat(result).isTrue()
+    }
 
-  @Test
-  fun shouldShowAppHandleEducation_focusAppNotInAllowlist_returnsFalse() = runTest {
-    // Pass Youtube as current focus app, it is not in allowlist hence #shouldShowAppHandleEducation
-    // should return false
-    testableResources.addOverride(
-        R.array.desktop_windowing_app_handle_education_allowlist_apps, arrayOf(GMAIL_PACKAGE_NAME))
-    val windowingEducationProto =
-        createWindowingEducationProto(
-            appUsageStats = mapOf(YOUTUBE_PACKAGE_NAME to 4),
-            appUsageStatsLastUpdateTimestampMillis = Long.MAX_VALUE)
-    val captionState =
-        createAppHandleState(createTaskInfo(runningTaskPackageName = YOUTUBE_PACKAGE_NAME))
-    `when`(datastoreRepository.windowingEducationProto()).thenReturn(windowingEducationProto)
+    @Test
+    fun shouldShowAppHandleEducation_focusAppNotInAllowlist_returnsFalse() = runTest {
+        // Pass Youtube as current focus app, it is not in allowlist hence
+        // #shouldShowAppHandleEducation
+        // should return false
+        testableResources.addOverride(
+            R.array.desktop_windowing_app_handle_education_allowlist_apps,
+            arrayOf(GMAIL_PACKAGE_NAME),
+        )
+        val windowingEducationProto =
+            createWindowingEducationProto(
+                appUsageStats = mapOf(YOUTUBE_PACKAGE_NAME to 4),
+                appUsageStatsLastUpdateTimestampMillis = Long.MAX_VALUE,
+            )
+        val captionState =
+            createAppHandleState(createTaskInfo(runningTaskPackageName = YOUTUBE_PACKAGE_NAME))
+        `when`(datastoreRepository.windowingEducationProto()).thenReturn(windowingEducationProto)
 
-    val result = educationFilter.shouldShowAppHandleEducation(captionState)
+        val result = educationFilter.shouldShowAppHandleEducation(captionState)
 
-    assertThat(result).isFalse()
-  }
+        assertThat(result).isFalse()
+    }
 
-  @Test
-  fun shouldShowAppHandleEducation_timeSinceSetupIsNotSufficient_returnsFalse() = runTest {
-    // Time required to have passed setup is > 100 years, hence #shouldShowAppHandleEducation should
-    // return false
-    testableResources.addOverride(
-        R.integer.desktop_windowing_education_required_time_since_setup_seconds, MAX_VALUE)
-    val windowingEducationProto =
-        createWindowingEducationProto(
-            appUsageStats = mapOf(GMAIL_PACKAGE_NAME to 4),
-            appUsageStatsLastUpdateTimestampMillis = Long.MAX_VALUE)
-    `when`(datastoreRepository.windowingEducationProto()).thenReturn(windowingEducationProto)
+    @Test
+    fun shouldShowAppHandleEducation_timeSinceSetupIsNotSufficient_returnsFalse() = runTest {
+        // Time required to have passed setup is > 100 years, hence #shouldShowAppHandleEducation
+        // should
+        // return false
+        testableResources.addOverride(
+            R.integer.desktop_windowing_education_required_time_since_setup_seconds,
+            MAX_VALUE,
+        )
+        val windowingEducationProto =
+            createWindowingEducationProto(
+                appUsageStats = mapOf(GMAIL_PACKAGE_NAME to 4),
+                appUsageStatsLastUpdateTimestampMillis = Long.MAX_VALUE,
+            )
+        `when`(datastoreRepository.windowingEducationProto()).thenReturn(windowingEducationProto)
 
-    val result = educationFilter.shouldShowAppHandleEducation(createAppHandleState())
+        val result = educationFilter.shouldShowAppHandleEducation(createAppHandleState())
 
-    assertThat(result).isFalse()
-  }
+        assertThat(result).isFalse()
+    }
 
-  @Test
-  fun shouldShowAppHandleEducation_appHandleHintViewedBefore_returnsFalse() = runTest {
-    // App handle hint has been viewed before, hence #shouldShowAppHandleEducation should return false
-    val windowingEducationProto =
-        createWindowingEducationProto(
-            appUsageStats = mapOf(GMAIL_PACKAGE_NAME to 4),
-            appHandleHintViewedTimestampMillis = 123L,
-            appUsageStatsLastUpdateTimestampMillis = Long.MAX_VALUE)
-    `when`(datastoreRepository.windowingEducationProto()).thenReturn(windowingEducationProto)
+    @Test
+    fun shouldShowAppHandleEducation_appHandleHintViewedBefore_returnsFalse() = runTest {
+        // App handle hint has been viewed before, hence #shouldShowAppHandleEducation should return
+        // false
+        val windowingEducationProto =
+            createWindowingEducationProto(
+                appUsageStats = mapOf(GMAIL_PACKAGE_NAME to 4),
+                appHandleHintViewedTimestampMillis = 123L,
+                appUsageStatsLastUpdateTimestampMillis = Long.MAX_VALUE,
+            )
+        `when`(datastoreRepository.windowingEducationProto()).thenReturn(windowingEducationProto)
 
-    val result = educationFilter.shouldShowAppHandleEducation(createAppHandleState())
+        val result = educationFilter.shouldShowAppHandleEducation(createAppHandleState())
 
-    assertThat(result).isFalse()
-  }
+        assertThat(result).isFalse()
+    }
 
-  @Test
-  fun shouldShowAppHandleEducation_appHandleHintUsedBefore_returnsFalse() = runTest {
-    // App handle hint has been used before, hence #shouldShowAppHandleEducation should return false
-    val windowingEducationProto =
-        createWindowingEducationProto(
-            appUsageStats = mapOf(GMAIL_PACKAGE_NAME to 4),
-            appHandleHintUsedTimestampMillis = 123L,
-            appUsageStatsLastUpdateTimestampMillis = Long.MAX_VALUE)
-    `when`(datastoreRepository.windowingEducationProto()).thenReturn(windowingEducationProto)
+    @Test
+    fun shouldShowAppHandleEducation_appHandleHintUsedBefore_returnsFalse() = runTest {
+        // App handle hint has been used before, hence #shouldShowAppHandleEducation should return
+        // false
+        val windowingEducationProto =
+            createWindowingEducationProto(
+                appUsageStats = mapOf(GMAIL_PACKAGE_NAME to 4),
+                appHandleHintUsedTimestampMillis = 123L,
+                appUsageStatsLastUpdateTimestampMillis = Long.MAX_VALUE,
+            )
+        `when`(datastoreRepository.windowingEducationProto()).thenReturn(windowingEducationProto)
 
-    val result = educationFilter.shouldShowAppHandleEducation(createAppHandleState())
+        val result = educationFilter.shouldShowAppHandleEducation(createAppHandleState())
 
-    assertThat(result).isFalse()
-  }
+        assertThat(result).isFalse()
+    }
 
-  @Test
-  fun shouldShowAppHandleEducation_doesNotHaveMinAppUsage_returnsFalse() = runTest {
-    // Simulate that gmail app has been launched twice before, minimum app launch count is 3, hence
-    // #shouldShowAppHandleEducation should return false
-    testableResources.addOverride(R.integer.desktop_windowing_education_min_app_launch_count, 3)
-    val windowingEducationProto =
-        createWindowingEducationProto(
-            appUsageStats = mapOf(GMAIL_PACKAGE_NAME to 2),
-            appUsageStatsLastUpdateTimestampMillis = Long.MAX_VALUE)
-    `when`(datastoreRepository.windowingEducationProto()).thenReturn(windowingEducationProto)
+    @Test
+    fun shouldShowAppHandleEducation_doesNotHaveMinAppUsage_returnsFalse() = runTest {
+        // Simulate that gmail app has been launched twice before, minimum app launch count is 3,
+        // hence
+        // #shouldShowAppHandleEducation should return false
+        testableResources.addOverride(R.integer.desktop_windowing_education_min_app_launch_count, 3)
+        val windowingEducationProto =
+            createWindowingEducationProto(
+                appUsageStats = mapOf(GMAIL_PACKAGE_NAME to 2),
+                appUsageStatsLastUpdateTimestampMillis = Long.MAX_VALUE,
+            )
+        `when`(datastoreRepository.windowingEducationProto()).thenReturn(windowingEducationProto)
 
-    val result = educationFilter.shouldShowAppHandleEducation(createAppHandleState())
+        val result = educationFilter.shouldShowAppHandleEducation(createAppHandleState())
 
-    assertThat(result).isFalse()
-  }
+        assertThat(result).isFalse()
+    }
 
-  @Test
-  fun shouldShowAppHandleEducation_appUsageStatsStale_queryAppUsageStats() = runTest {
-    // UsageStats caching interval is set to 0ms, that means caching should happen very frequently
-    testableResources.addOverride(
-        R.integer.desktop_windowing_education_app_usage_cache_interval_seconds, 0)
-    // The DataStore currently holds a proto object where Gmail's app launch count is recorded as 4.
-    // This value exceeds the minimum required count of 3.
-    testableResources.addOverride(R.integer.desktop_windowing_education_min_app_launch_count, 3)
-    val windowingEducationProto =
-        createWindowingEducationProto(
-            appUsageStats = mapOf(GMAIL_PACKAGE_NAME to 4),
-            appUsageStatsLastUpdateTimestampMillis = 0)
-    // The mocked UsageStatsManager is configured to return a launch count of 2 for Gmail.
-    // This value is below the minimum required count of 3.
-    `when`(mockUsageStatsManager.queryAndAggregateUsageStats(anyLong(), anyLong()))
-        .thenReturn(mapOf(GMAIL_PACKAGE_NAME to UsageStats().apply { mAppLaunchCount = 2 }))
-    `when`(datastoreRepository.windowingEducationProto()).thenReturn(windowingEducationProto)
+    @Test
+    fun shouldShowAppHandleEducation_appUsageStatsStale_queryAppUsageStats() = runTest {
+        // UsageStats caching interval is set to 0ms, that means caching should happen very
+        // frequently
+        testableResources.addOverride(
+            R.integer.desktop_windowing_education_app_usage_cache_interval_seconds,
+            0,
+        )
+        // The DataStore currently holds a proto object where Gmail's app launch count is recorded
+        // as 4.
+        // This value exceeds the minimum required count of 3.
+        testableResources.addOverride(R.integer.desktop_windowing_education_min_app_launch_count, 3)
+        val windowingEducationProto =
+            createWindowingEducationProto(
+                appUsageStats = mapOf(GMAIL_PACKAGE_NAME to 4),
+                appUsageStatsLastUpdateTimestampMillis = 0,
+            )
+        // The mocked UsageStatsManager is configured to return a launch count of 2 for Gmail.
+        // This value is below the minimum required count of 3.
+        `when`(mockUsageStatsManager.queryAndAggregateUsageStats(anyLong(), anyLong()))
+            .thenReturn(mapOf(GMAIL_PACKAGE_NAME to UsageStats().apply { mAppLaunchCount = 2 }))
+        `when`(datastoreRepository.windowingEducationProto()).thenReturn(windowingEducationProto)
 
-    val result = educationFilter.shouldShowAppHandleEducation(createAppHandleState())
+        val result = educationFilter.shouldShowAppHandleEducation(createAppHandleState())
 
-    // Result should be false as queried usage stats should be considered to determine the result
-    // instead of cached stats
-    assertThat(result).isFalse()
-  }
+        // Result should be false as queried usage stats should be considered to determine the
+        // result
+        // instead of cached stats
+        assertThat(result).isFalse()
+    }
 
-  @Test
-  fun shouldShowAppHandleEducation_appHandleMenuExpanded_returnsFalse() = runTest {
-    val windowingEducationProto =
-        createWindowingEducationProto(
-            appUsageStats = mapOf(GMAIL_PACKAGE_NAME to 4),
-            appUsageStatsLastUpdateTimestampMillis = Long.MAX_VALUE)
-    // Simulate app handle menu is expanded
-    val captionState = createAppHandleState(isHandleMenuExpanded = true)
-    `when`(datastoreRepository.windowingEducationProto()).thenReturn(windowingEducationProto)
+    @Test
+    fun shouldShowAppHandleEducation_appHandleMenuExpanded_returnsFalse() = runTest {
+        val windowingEducationProto =
+            createWindowingEducationProto(
+                appUsageStats = mapOf(GMAIL_PACKAGE_NAME to 4),
+                appUsageStatsLastUpdateTimestampMillis = Long.MAX_VALUE,
+            )
+        // Simulate app handle menu is expanded
+        val captionState = createAppHandleState(isHandleMenuExpanded = true)
+        `when`(datastoreRepository.windowingEducationProto()).thenReturn(windowingEducationProto)
 
-    val result = educationFilter.shouldShowAppHandleEducation(captionState)
+        val result = educationFilter.shouldShowAppHandleEducation(captionState)
 
-    // We should not show app handle education if app menu is expanded
-    assertThat(result).isFalse()
-  }
+        // We should not show app handle education if app menu is expanded
+        assertThat(result).isFalse()
+    }
 
-  @Test
-  fun shouldShowAppHandleEducation_overridePrerequisite_returnsTrue() = runTest {
-    // Simulate that gmail app has been launched twice before, minimum app launch count is 3, hence
-    // #shouldShowAppHandleEducation should return false. But as we are overriding prerequisite
-    // conditions, #shouldShowAppHandleEducation should return true.
-    testableResources.addOverride(R.integer.desktop_windowing_education_min_app_launch_count, 3)
-    val systemPropertiesKey = "persist.desktop_windowing_app_handle_education_override_conditions"
-    whenever(SystemProperties.getBoolean(eq(systemPropertiesKey), anyBoolean())).thenReturn(true)
-    val windowingEducationProto =
-        createWindowingEducationProto(
-            appUsageStats = mapOf(GMAIL_PACKAGE_NAME to 2),
-            appUsageStatsLastUpdateTimestampMillis = Long.MAX_VALUE)
-    `when`(datastoreRepository.windowingEducationProto()).thenReturn(windowingEducationProto)
+    @Test
+    fun shouldShowAppHandleEducation_overridePrerequisite_returnsTrue() = runTest {
+        // Simulate that gmail app has been launched twice before, minimum app launch count is 3,
+        // hence
+        // #shouldShowAppHandleEducation should return false. But as we are overriding prerequisite
+        // conditions, #shouldShowAppHandleEducation should return true.
+        testableResources.addOverride(R.integer.desktop_windowing_education_min_app_launch_count, 3)
+        val systemPropertiesKey =
+            "persist.desktop_windowing_app_handle_education_override_conditions"
+        whenever(SystemProperties.getBoolean(eq(systemPropertiesKey), anyBoolean()))
+            .thenReturn(true)
+        val windowingEducationProto =
+            createWindowingEducationProto(
+                appUsageStats = mapOf(GMAIL_PACKAGE_NAME to 2),
+                appUsageStatsLastUpdateTimestampMillis = Long.MAX_VALUE,
+            )
+        `when`(datastoreRepository.windowingEducationProto()).thenReturn(windowingEducationProto)
 
-    val result = educationFilter.shouldShowAppHandleEducation(createAppHandleState())
+        val result = educationFilter.shouldShowAppHandleEducation(createAppHandleState())
 
-    assertThat(result).isTrue()
-  }
+        assertThat(result).isTrue()
+    }
 }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/minimize/DesktopWindowLimitRemoteHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/minimize/DesktopWindowLimitRemoteHandlerTest.kt
index 6a5d9f6..8d7fb5d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/minimize/DesktopWindowLimitRemoteHandlerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/minimize/DesktopWindowLimitRemoteHandlerTest.kt
@@ -66,16 +66,27 @@
 
     private fun createRemoteHandler(taskIdToMinimize: Int) =
         DesktopWindowLimitRemoteHandler(
-            shellExecutor, rootTaskDisplayAreaOrganizer, remoteTransition, taskIdToMinimize)
+            shellExecutor,
+            rootTaskDisplayAreaOrganizer,
+            remoteTransition,
+            taskIdToMinimize,
+        )
 
     @Test
     fun startAnimation_dontSetTransition_returnsFalse() {
         val minimizeTask = createDesktopTask()
         val remoteHandler = createRemoteHandler(taskIdToMinimize = minimizeTask.taskId)
 
-        assertThat(remoteHandler.startAnimation(transition,
-            createMinimizeTransitionInfo(minimizeTask), startT, finishT, finishCallback)
-        ).isFalse()
+        assertThat(
+                remoteHandler.startAnimation(
+                    transition,
+                    createMinimizeTransitionInfo(minimizeTask),
+                    startT,
+                    finishT,
+                    finishCallback,
+                )
+            )
+            .isFalse()
     }
 
     @Test
@@ -84,9 +95,8 @@
         remoteHandler.setTransition(transition)
         val info = createToFrontTransitionInfo()
 
-        assertThat(
-            remoteHandler.startAnimation(transition, info, startT, finishT, finishCallback)
-        ).isFalse()
+        assertThat(remoteHandler.startAnimation(transition, info, startT, finishT, finishCallback))
+            .isFalse()
     }
 
     @Test
@@ -96,9 +106,8 @@
         remoteHandler.setTransition(transition)
         val info = createMinimizeTransitionInfo(minimizeTask)
 
-        assertThat(
-            remoteHandler.startAnimation(transition, info, startT, finishT, finishCallback)
-        ).isTrue()
+        assertThat(remoteHandler.startAnimation(transition, info, startT, finishT, finishCallback))
+            .isTrue()
     }
 
     @Test
@@ -109,8 +118,7 @@
 
         remoteHandler.startAnimation(transition, info, startT, finishT, finishCallback)
 
-        verify(rootTaskDisplayAreaOrganizer, times(0))
-            .reparentToDisplayArea(anyInt(), any(), any())
+        verify(rootTaskDisplayAreaOrganizer, times(0)).reparentToDisplayArea(anyInt(), any(), any())
     }
 
     @Test
@@ -154,14 +162,18 @@
 
     private fun createToFrontTransitionInfo() =
         TransitionInfoBuilder(TRANSIT_TO_FRONT)
-            .addChange(TRANSIT_TO_FRONT,
-                TestRunningTaskInfoBuilder().setWindowingMode(WINDOWING_MODE_FREEFORM).build())
+            .addChange(
+                TRANSIT_TO_FRONT,
+                TestRunningTaskInfoBuilder().setWindowingMode(WINDOWING_MODE_FREEFORM).build(),
+            )
             .build()
 
     private fun createMinimizeTransitionInfo(minimizeTask: ActivityManager.RunningTaskInfo) =
         TransitionInfoBuilder(TRANSIT_TO_FRONT)
-            .addChange(TRANSIT_TO_FRONT,
-                TestRunningTaskInfoBuilder().setWindowingMode(WINDOWING_MODE_FREEFORM).build())
+            .addChange(
+                TRANSIT_TO_FRONT,
+                TestRunningTaskInfoBuilder().setWindowingMode(WINDOWING_MODE_FREEFORM).build(),
+            )
             .addChange(TRANSIT_TO_BACK, minimizeTask)
             .build()
 }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/persistence/DesktopPersistentRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/persistence/DesktopPersistentRepositoryTest.kt
index 4f7e80c..eae2066 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/persistence/DesktopPersistentRepositoryTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/persistence/DesktopPersistentRepositoryTest.kt
@@ -63,9 +63,10 @@
             DataStoreFactory.create(
                 serializer =
                     DesktopPersistentRepository.Companion.DesktopPersistentRepositoriesSerializer,
-                scope = datastoreScope) {
-                    testContext.dataStoreFile(DESKTOP_REPOSITORY_STATES_DATASTORE_TEST_FILE)
-                }
+                scope = datastoreScope,
+            ) {
+                testContext.dataStoreFile(DESKTOP_REPOSITORY_STATES_DATASTORE_TEST_FILE)
+            }
         datastoreRepository = DesktopPersistentRepository(testDatastore)
     }
 
@@ -113,7 +114,8 @@
                 visibleTasks = visibleTasks,
                 minimizedTasks = minimizedTasks,
                 freeformTasksInZOrder = freeformTasksInZOrder,
-                userId = DEFAULT_USER_ID)
+                userId = DEFAULT_USER_ID,
+            )
 
             val actualDesktop = datastoreRepository.readDesktop(DEFAULT_USER_ID, DEFAULT_DESKTOP_ID)
             assertThat(actualDesktop?.tasksByTaskIdMap).hasSize(2)
@@ -137,7 +139,8 @@
                 visibleTasks = visibleTasks,
                 minimizedTasks = minimizedTasks,
                 freeformTasksInZOrder = freeformTasksInZOrder,
-                userId = DEFAULT_USER_ID)
+                userId = DEFAULT_USER_ID,
+            )
 
             val actualDesktop = datastoreRepository.readDesktop(DEFAULT_USER_ID, DEFAULT_DESKTOP_ID)
             assertThat(actualDesktop?.tasksByTaskIdMap?.get(task.taskId)?.desktopTaskState)
@@ -161,7 +164,8 @@
                 visibleTasks = visibleTasks,
                 minimizedTasks = minimizedTasks,
                 freeformTasksInZOrder = freeformTasksInZOrder,
-                userId = DEFAULT_USER_ID)
+                userId = DEFAULT_USER_ID,
+            )
 
             val actualDesktop = datastoreRepository.readDesktop(DEFAULT_USER_ID, DEFAULT_DESKTOP_ID)
             assertThat(actualDesktop?.tasksByTaskIdMap).isEmpty()
@@ -194,7 +198,7 @@
 
         fun createDesktopTask(
             taskId: Int,
-            state: DesktopTaskState = DesktopTaskState.VISIBLE
+            state: DesktopTaskState = DesktopTaskState.VISIBLE,
         ): DesktopTask =
             DesktopTask.newBuilder().setTaskId(taskId).setDesktopTaskState(state).build()
     }
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
index 1c88a29..a3c4416 100644
--- 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
@@ -27,6 +27,7 @@
 import com.android.wm.shell.ShellTestCase
 import com.android.wm.shell.common.ShellExecutor
 import com.android.wm.shell.desktopmode.DesktopUserRepositories
+import com.android.wm.shell.sysui.ShellController
 import com.android.wm.shell.sysui.ShellInit
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.CoroutineScope
@@ -46,15 +47,12 @@
 import org.mockito.kotlin.mock
 import org.mockito.kotlin.whenever
 
-
 @SmallTest
 @RunWith(AndroidTestingRunner::class)
 @ExperimentalCoroutinesApi
 class DesktopRepositoryInitializerTest : ShellTestCase() {
 
-    @JvmField
-    @Rule
-    val setFlagsRule = SetFlagsRule()
+    @JvmField @Rule val setFlagsRule = SetFlagsRule()
 
     private lateinit var repositoryInitializer: DesktopRepositoryInitializer
     private lateinit var shellInit: ShellInit
@@ -64,6 +62,7 @@
     private val persistentRepository = mock<DesktopPersistentRepository>()
     private val userManager = mock<UserManager>()
     private val testExecutor = mock<ShellExecutor>()
+    private val shellController = mock<ShellController>()
 
     @Before
     fun setUp() {
@@ -74,8 +73,13 @@
             DesktopRepositoryInitializerImpl(context, persistentRepository, datastoreScope)
         desktopUserRepositories =
             DesktopUserRepositories(
-                context, shellInit, persistentRepository, repositoryInitializer, datastoreScope,
-                userManager
+                context,
+                shellInit,
+                shellController,
+                persistentRepository,
+                repositoryInitializer,
+                datastoreScope,
+                userManager,
             )
     }
 
@@ -83,101 +87,94 @@
     @EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_PERSISTENCE, FLAG_ENABLE_DESKTOP_WINDOWING_HSUM)
     fun initWithPersistence_multipleUsers_addedCorrectly() =
         runTest(StandardTestDispatcher()) {
-            whenever(persistentRepository.getUserDesktopRepositoryMap()).thenReturn(
-                mapOf(
-                    USER_ID_1 to desktopRepositoryState1,
-                    USER_ID_2 to desktopRepositoryState2
+            whenever(persistentRepository.getUserDesktopRepositoryMap())
+                .thenReturn(
+                    mapOf(
+                        USER_ID_1 to desktopRepositoryState1,
+                        USER_ID_2 to desktopRepositoryState2,
+                    )
                 )
-            )
             whenever(persistentRepository.getDesktopRepositoryState(USER_ID_1))
                 .thenReturn(desktopRepositoryState1)
             whenever(persistentRepository.getDesktopRepositoryState(USER_ID_2))
                 .thenReturn(desktopRepositoryState2)
-            whenever(persistentRepository.readDesktop(USER_ID_1, DESKTOP_ID_1))
-                .thenReturn(desktop1)
-            whenever(persistentRepository.readDesktop(USER_ID_1, DESKTOP_ID_2))
-                .thenReturn(desktop2)
-            whenever(persistentRepository.readDesktop(USER_ID_2, DESKTOP_ID_3))
-                .thenReturn(desktop3)
+            whenever(persistentRepository.readDesktop(USER_ID_1, DESKTOP_ID_1)).thenReturn(desktop1)
+            whenever(persistentRepository.readDesktop(USER_ID_1, DESKTOP_ID_2)).thenReturn(desktop2)
+            whenever(persistentRepository.readDesktop(USER_ID_2, DESKTOP_ID_3)).thenReturn(desktop3)
 
             repositoryInitializer.initialize(desktopUserRepositories)
 
             // Desktop Repository currently returns all tasks across desktops for a specific user
-            // since the repository currently doesn't handle desktops. This test logic should be updated
+            // since the repository currently doesn't handle desktops. This test logic should be
+            // updated
             // once the repository handles multiple desktops.
             assertThat(
-                desktopUserRepositories.getProfile(USER_ID_1)
-                    .getActiveTasks(DEFAULT_DISPLAY)
-            )
+                    desktopUserRepositories.getProfile(USER_ID_1).getActiveTasks(DEFAULT_DISPLAY)
+                )
                 .containsExactly(1, 3, 4, 5)
                 .inOrder()
             assertThat(
-                desktopUserRepositories.getProfile(USER_ID_1)
-                    .getExpandedTasksOrdered(DEFAULT_DISPLAY)
-            )
+                    desktopUserRepositories
+                        .getProfile(USER_ID_1)
+                        .getExpandedTasksOrdered(DEFAULT_DISPLAY)
+                )
                 .containsExactly(5, 1)
                 .inOrder()
             assertThat(
-                desktopUserRepositories.getProfile(USER_ID_1)
-                    .getMinimizedTasks(DEFAULT_DISPLAY)
-            )
+                    desktopUserRepositories.getProfile(USER_ID_1).getMinimizedTasks(DEFAULT_DISPLAY)
+                )
                 .containsExactly(3, 4)
                 .inOrder()
 
             assertThat(
-                desktopUserRepositories.getProfile(USER_ID_2)
-                    .getActiveTasks(DEFAULT_DISPLAY)
-            )
+                    desktopUserRepositories.getProfile(USER_ID_2).getActiveTasks(DEFAULT_DISPLAY)
+                )
                 .containsExactly(7, 8)
                 .inOrder()
             assertThat(
-                desktopUserRepositories.getProfile(USER_ID_2)
-                    .getExpandedTasksOrdered(DEFAULT_DISPLAY)
-            )
+                    desktopUserRepositories
+                        .getProfile(USER_ID_2)
+                        .getExpandedTasksOrdered(DEFAULT_DISPLAY)
+                )
                 .contains(7)
             assertThat(
-                desktopUserRepositories.getProfile(USER_ID_2)
-                    .getMinimizedTasks(DEFAULT_DISPLAY)
-            ).containsExactly(8)
+                    desktopUserRepositories.getProfile(USER_ID_2).getMinimizedTasks(DEFAULT_DISPLAY)
+                )
+                .containsExactly(8)
         }
 
     @Test
     @EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_PERSISTENCE)
     fun initWithPersistence_singleUser_addedCorrectly() =
         runTest(StandardTestDispatcher()) {
-            whenever(persistentRepository.getUserDesktopRepositoryMap()).thenReturn(
-                mapOf(
-                    USER_ID_1 to desktopRepositoryState1,
-                )
-            )
+            whenever(persistentRepository.getUserDesktopRepositoryMap())
+                .thenReturn(mapOf(USER_ID_1 to desktopRepositoryState1))
             whenever(persistentRepository.getDesktopRepositoryState(USER_ID_1))
                 .thenReturn(desktopRepositoryState1)
-            whenever(persistentRepository.readDesktop(USER_ID_1, DESKTOP_ID_1))
-                .thenReturn(desktop1)
-            whenever(persistentRepository.readDesktop(USER_ID_1, DESKTOP_ID_2))
-                .thenReturn(desktop2)
+            whenever(persistentRepository.readDesktop(USER_ID_1, DESKTOP_ID_1)).thenReturn(desktop1)
+            whenever(persistentRepository.readDesktop(USER_ID_1, DESKTOP_ID_2)).thenReturn(desktop2)
 
             repositoryInitializer.initialize(desktopUserRepositories)
 
             // Desktop Repository currently returns all tasks across desktops for a specific user
-            // since the repository currently doesn't handle desktops. This test logic should be updated
+            // since the repository currently doesn't handle desktops. This test logic should be
+            // updated
             // once the repository handles multiple desktops.
             assertThat(
-                desktopUserRepositories.getProfile(USER_ID_1)
-                    .getActiveTasks(DEFAULT_DISPLAY)
-            )
+                    desktopUserRepositories.getProfile(USER_ID_1).getActiveTasks(DEFAULT_DISPLAY)
+                )
                 .containsExactly(1, 3, 4, 5)
                 .inOrder()
             assertThat(
-                desktopUserRepositories.getProfile(USER_ID_1)
-                    .getExpandedTasksOrdered(DEFAULT_DISPLAY)
-            )
+                    desktopUserRepositories
+                        .getProfile(USER_ID_1)
+                        .getExpandedTasksOrdered(DEFAULT_DISPLAY)
+                )
                 .containsExactly(5, 1)
                 .inOrder()
             assertThat(
-                desktopUserRepositories.getProfile(USER_ID_1)
-                    .getMinimizedTasks(DEFAULT_DISPLAY)
-            )
+                    desktopUserRepositories.getProfile(USER_ID_1).getMinimizedTasks(DEFAULT_DISPLAY)
+                )
                 .containsExactly(3, 4)
                 .inOrder()
         }
@@ -195,70 +192,73 @@
         const val DESKTOP_ID_3 = 4
 
         val freeformTasksInZOrder1 = listOf(1, 3)
-        val desktop1: Desktop = Desktop.newBuilder()
-            .setDesktopId(DESKTOP_ID_1)
-            .addAllZOrderedTasks(freeformTasksInZOrder1)
-            .putTasksByTaskId(
-                1,
-                DesktopTask.newBuilder()
-                    .setTaskId(1)
-                    .setDesktopTaskState(DesktopTaskState.VISIBLE)
-                    .build()
-            )
-            .putTasksByTaskId(
-                3,
-                DesktopTask.newBuilder()
-                    .setTaskId(3)
-                    .setDesktopTaskState(DesktopTaskState.MINIMIZED)
-                    .build()
-            )
-            .build()
+        val desktop1: Desktop =
+            Desktop.newBuilder()
+                .setDesktopId(DESKTOP_ID_1)
+                .addAllZOrderedTasks(freeformTasksInZOrder1)
+                .putTasksByTaskId(
+                    1,
+                    DesktopTask.newBuilder()
+                        .setTaskId(1)
+                        .setDesktopTaskState(DesktopTaskState.VISIBLE)
+                        .build(),
+                )
+                .putTasksByTaskId(
+                    3,
+                    DesktopTask.newBuilder()
+                        .setTaskId(3)
+                        .setDesktopTaskState(DesktopTaskState.MINIMIZED)
+                        .build(),
+                )
+                .build()
 
         val freeformTasksInZOrder2 = listOf(4, 5)
-        val desktop2: Desktop = Desktop.newBuilder()
-            .setDesktopId(DESKTOP_ID_2)
-            .addAllZOrderedTasks(freeformTasksInZOrder2)
-            .putTasksByTaskId(
-                4,
-                DesktopTask.newBuilder()
-                    .setTaskId(4)
-                    .setDesktopTaskState(DesktopTaskState.MINIMIZED)
-                    .build()
-            )
-            .putTasksByTaskId(
-                5,
-                DesktopTask.newBuilder()
-                    .setTaskId(5)
-                    .setDesktopTaskState(DesktopTaskState.VISIBLE)
-                    .build()
-            )
-            .build()
+        val desktop2: Desktop =
+            Desktop.newBuilder()
+                .setDesktopId(DESKTOP_ID_2)
+                .addAllZOrderedTasks(freeformTasksInZOrder2)
+                .putTasksByTaskId(
+                    4,
+                    DesktopTask.newBuilder()
+                        .setTaskId(4)
+                        .setDesktopTaskState(DesktopTaskState.MINIMIZED)
+                        .build(),
+                )
+                .putTasksByTaskId(
+                    5,
+                    DesktopTask.newBuilder()
+                        .setTaskId(5)
+                        .setDesktopTaskState(DesktopTaskState.VISIBLE)
+                        .build(),
+                )
+                .build()
 
         val freeformTasksInZOrder3 = listOf(7, 8)
-        val desktop3: Desktop = Desktop.newBuilder()
-            .setDesktopId(DESKTOP_ID_3)
-            .addAllZOrderedTasks(freeformTasksInZOrder3)
-            .putTasksByTaskId(
-                7,
-                DesktopTask.newBuilder()
-                    .setTaskId(7)
-                    .setDesktopTaskState(DesktopTaskState.VISIBLE)
-                    .build()
-            )
-            .putTasksByTaskId(
-                8,
-                DesktopTask.newBuilder()
-                    .setTaskId(8)
-                    .setDesktopTaskState(DesktopTaskState.MINIMIZED)
-                    .build()
-            )
-            .build()
-        val desktopRepositoryState1: DesktopRepositoryState = DesktopRepositoryState.newBuilder()
-            .putDesktop(DESKTOP_ID_1, desktop1)
-            .putDesktop(DESKTOP_ID_2, desktop2)
-            .build()
-        val desktopRepositoryState2: DesktopRepositoryState = DesktopRepositoryState.newBuilder()
-            .putDesktop(DESKTOP_ID_3, desktop3)
-            .build()
+        val desktop3: Desktop =
+            Desktop.newBuilder()
+                .setDesktopId(DESKTOP_ID_3)
+                .addAllZOrderedTasks(freeformTasksInZOrder3)
+                .putTasksByTaskId(
+                    7,
+                    DesktopTask.newBuilder()
+                        .setTaskId(7)
+                        .setDesktopTaskState(DesktopTaskState.VISIBLE)
+                        .build(),
+                )
+                .putTasksByTaskId(
+                    8,
+                    DesktopTask.newBuilder()
+                        .setTaskId(8)
+                        .setDesktopTaskState(DesktopTaskState.MINIMIZED)
+                        .build(),
+                )
+                .build()
+        val desktopRepositoryState1: DesktopRepositoryState =
+            DesktopRepositoryState.newBuilder()
+                .putDesktop(DESKTOP_ID_1, desktop1)
+                .putDesktop(DESKTOP_ID_2, desktop2)
+                .build()
+        val desktopRepositoryState2: DesktopRepositoryState =
+            DesktopRepositoryState.newBuilder().putDesktop(DESKTOP_ID_3, desktop3).build()
     }
 }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
index bb9703f..bca9c3f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
@@ -106,6 +106,7 @@
     @Mock RootTaskDisplayAreaOrganizer mRootTDAOrganizer;
     @Mock ShellExecutor mMainExecutor;
     @Mock Handler mMainHandler;
+    @Mock ShellExecutor mBgExecutor;
     @Mock DisplayController mDisplayController;
     @Mock DisplayImeController mDisplayImeController;
     @Mock DisplayInsetsController mDisplayInsetsController;
@@ -137,7 +138,8 @@
                 mDisplayInsetsController, mDragAndDropController, mTransitions, mTransactionPool,
                 mIconProvider, Optional.of(mRecentTasks), mLaunchAdjacentController,
                 Optional.of(mWindowDecorViewModel), Optional.of(mDesktopTasksController),
-                mStageCoordinator, mMultiInstanceHelper, mSplitState, mMainExecutor, mMainHandler));
+                mStageCoordinator, mMultiInstanceHelper, mSplitState, mMainExecutor, mMainHandler,
+                mBgExecutor));
     }
 
     @Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
index 1a2d60d..ada7b4af 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
@@ -36,6 +36,7 @@
 import com.android.wm.shell.common.SyncTransactionQueue;
 import com.android.wm.shell.common.split.SplitLayout;
 import com.android.wm.shell.common.split.SplitState;
+import com.android.wm.shell.desktopmode.DesktopTasksController;
 import com.android.wm.shell.recents.RecentTasksController;
 import com.android.wm.shell.shared.TransactionPool;
 import com.android.wm.shell.transition.Transitions;
@@ -78,14 +79,16 @@
                 StageTaskListener sideStage, DisplayController displayController,
                 DisplayImeController imeController, DisplayInsetsController insetsController,
                 SplitLayout splitLayout, Transitions transitions, TransactionPool transactionPool,
-                ShellExecutor mainExecutor, Handler mainHandler,
+                ShellExecutor mainExecutor, Handler mainHandler, ShellExecutor bgExecutor,
                 Optional<RecentTasksController> recentTasks,
                 LaunchAdjacentController launchAdjacentController,
-                Optional<WindowDecorViewModel> windowDecorViewModel, SplitState splitState) {
+                Optional<WindowDecorViewModel> windowDecorViewModel, SplitState splitState,
+                Optional<DesktopTasksController> desktopTasksController) {
             super(context, displayId, syncQueue, taskOrganizer, mainStage,
                     sideStage, displayController, imeController, insetsController, splitLayout,
-                    transitions, transactionPool, mainExecutor, mainHandler, recentTasks,
-                    launchAdjacentController, windowDecorViewModel, splitState);
+                    transitions, transactionPool, mainExecutor, mainHandler, bgExecutor,
+                    recentTasks, launchAdjacentController, windowDecorViewModel, splitState,
+                    desktopTasksController);
 
             // Prepare root task for testing.
             mRootTask = new TestRunningTaskInfoBuilder().build();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
index de77837..ffa8b60 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
@@ -111,6 +111,7 @@
     @Mock private WindowDecorViewModel mWindowDecorViewModel;
     @Mock private SplitState mSplitState;
     @Mock private ShellExecutor mMainExecutor;
+    @Mock private ShellExecutor mBgExecutor;
     @Mock private Handler mMainHandler;
     @Mock private LaunchAdjacentController mLaunchAdjacentController;
     @Mock private DefaultMixedHandler mMixedHandler;
@@ -136,17 +137,19 @@
         mSplitLayout = SplitTestUtils.createMockSplitLayout();
         mMainStage = spy(new StageTaskListener(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mock(
                 StageTaskListener.StageListenerCallbacks.class), mSyncQueue,
-                mIconProvider, Optional.of(mWindowDecorViewModel), STAGE_TYPE_MAIN));
+                mIconProvider, mMainExecutor, mBgExecutor, Optional.of(mWindowDecorViewModel),
+                STAGE_TYPE_MAIN));
         mMainStage.onTaskAppeared(new TestRunningTaskInfoBuilder().build(), createMockSurface());
         mSideStage = spy(new StageTaskListener(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mock(
                 StageTaskListener.StageListenerCallbacks.class), mSyncQueue,
-                mIconProvider, Optional.of(mWindowDecorViewModel), STAGE_TYPE_SIDE));
+                mIconProvider, mMainExecutor, mBgExecutor, Optional.of(mWindowDecorViewModel),
+                STAGE_TYPE_SIDE));
         mSideStage.onTaskAppeared(new TestRunningTaskInfoBuilder().build(), createMockSurface());
         mStageCoordinator = new SplitTestUtils.TestStageCoordinator(mContext, DEFAULT_DISPLAY,
                 mSyncQueue, mTaskOrganizer, mMainStage, mSideStage, mDisplayController,
                 mDisplayImeController, mDisplayInsetsController, mSplitLayout, mTransitions,
-                mTransactionPool, mMainExecutor, mMainHandler, Optional.empty(),
-                mLaunchAdjacentController, Optional.empty(), mSplitState);
+                mTransactionPool, mMainExecutor, mMainHandler, mBgExecutor, Optional.empty(),
+                mLaunchAdjacentController, Optional.empty(), mSplitState, Optional.empty());
         mStageCoordinator.setMixedHandler(mMixedHandler);
         mSplitScreenTransitions = mStageCoordinator.getSplitTransitions();
         doAnswer((Answer<IBinder>) invocation -> mock(IBinder.class))
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
index 7afcce1..9d1df86 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
@@ -119,6 +119,8 @@
     private DefaultMixedHandler mDefaultMixedHandler;
     @Mock
     private SplitState mSplitState;
+    @Mock
+    private ShellExecutor mBgExecutor;
 
     private final Rect mBounds1 = new Rect(10, 20, 30, 40);
     private final Rect mBounds2 = new Rect(5, 10, 15, 20);
@@ -141,8 +143,9 @@
         mStageCoordinator = spy(new StageCoordinator(mContext, DEFAULT_DISPLAY, mSyncQueue,
                 mTaskOrganizer, mMainStage, mSideStage, mDisplayController, mDisplayImeController,
                 mDisplayInsetsController, mSplitLayout, mTransitions, mTransactionPool,
-                mMainExecutor, mMainHandler, Optional.empty(), mLaunchAdjacentController,
-                Optional.empty(), mSplitState));
+                mMainExecutor, mMainHandler, mBgExecutor, Optional.empty(),
+                mLaunchAdjacentController, Optional.empty(), mSplitState,
+                Optional.empty()));
         mDividerLeash = new SurfaceControl.Builder().setName("fakeDivider").build();
 
         when(mSplitLayout.getTopLeftBounds()).thenReturn(mBounds1);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageOrderOperatorTests.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageOrderOperatorTests.kt
index 3b4a86a..62b830d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageOrderOperatorTests.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageOrderOperatorTests.kt
@@ -23,6 +23,7 @@
 import com.android.wm.shell.Flags.enableFlexibleSplit
 import com.android.wm.shell.ShellTaskOrganizer
 import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.common.ShellExecutor
 import com.android.wm.shell.common.SyncTransactionQueue
 import com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_33_66
 import com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_50_50
@@ -41,6 +42,10 @@
 class StageOrderOperatorTests : ShellTestCase() {
 
     @Mock
+    lateinit var mMainExecutor: ShellExecutor
+    @Mock
+    lateinit var mBgExecutor: ShellExecutor
+    @Mock
     lateinit var mTaskOrganizer: ShellTaskOrganizer
     @Mock
     lateinit var mSyncQueue: SyncTransactionQueue
@@ -62,6 +67,8 @@
             stageListenerCallbacks,
             mSyncQueue,
             iconProvider,
+            mMainExecutor,
+            mBgExecutor,
             windowDecorViewModel,
             )
         assert(stageOrderOperator.activeStages.size == 0)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java
index fe91440..effc6a7 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java
@@ -43,6 +43,7 @@
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.ShellTestCase;
 import com.android.wm.shell.TestRunningTaskInfoBuilder;
+import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.SyncTransactionQueue;
 import com.android.wm.shell.windowdecor.WindowDecorViewModel;
 
@@ -73,6 +74,10 @@
     @Mock
     private SyncTransactionQueue mSyncQueue;
     @Mock
+    private ShellExecutor mMainExecutor;
+    @Mock
+    private ShellExecutor mBgExecutor;
+    @Mock
     private IconProvider mIconProvider;
     @Mock
     private WindowDecorViewModel mWindowDecorViewModel;
@@ -95,6 +100,8 @@
                 mCallbacks,
                 mSyncQueue,
                 mIconProvider,
+                mMainExecutor,
+                mBgExecutor,
                 Optional.of(mWindowDecorViewModel),
                 STAGE_TYPE_UNDEFINED);
         mRootTask = new TestRunningTaskInfoBuilder().build();
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 e871711..cf6c3a5e 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
@@ -63,6 +63,7 @@
         userRepositories = DesktopUserRepositories(
             context = context,
             shellInit = ShellInit(TestShellExecutor()),
+            shellController = mock(),
             persistentRepository = mock(),
             repositoryInitializer = mock(),
             mainCoroutineScope = mock(),
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 0214da4..aead0a7 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
@@ -1015,11 +1015,11 @@
             onCaptionButtonTouchListener = onTouchListenerCaptor
         )
 
-        whenever(mockTaskPositioner.onDragPositioningStart(any(), any(), any()))
+        whenever(mockTaskPositioner.onDragPositioningStart(any(), any(), any(), any()))
             .thenReturn(INITIAL_BOUNDS)
-        whenever(mockTaskPositioner.onDragPositioningMove(any(), any()))
+        whenever(mockTaskPositioner.onDragPositioningMove(any(), any(), any()))
             .thenReturn(INITIAL_BOUNDS)
-        whenever(mockTaskPositioner.onDragPositioningEnd(any(), any()))
+        whenever(mockTaskPositioner.onDragPositioningEnd(any(), any(), any()))
             .thenReturn(INITIAL_BOUNDS)
 
         val view = mock(View::class.java)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt
index 7a37c5e..b5e8ceb 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt
@@ -74,6 +74,7 @@
 import com.android.wm.shell.util.StubTransaction
 import com.android.wm.shell.windowdecor.DesktopModeWindowDecorViewModel.DesktopModeKeyguardChangeListener
 import com.android.wm.shell.windowdecor.DesktopModeWindowDecorViewModel.DesktopModeOnInsetsChangedListener
+import com.android.wm.shell.windowdecor.common.WindowDecorTaskResourceLoader
 import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHost
 import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHostSupplier
 import com.android.wm.shell.windowdecor.viewholder.AppHeaderViewHolder
@@ -91,6 +92,8 @@
 import org.mockito.kotlin.whenever
 import java.util.Optional
 import java.util.function.Supplier
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.MainCoroutineDispatcher
 
 /**
  * Utility class for tests of [DesktopModeWindowDecorViewModel]
@@ -181,6 +184,8 @@
             testShellExecutor,
             mockMainHandler,
             mockMainChoreographer,
+            mock<MainCoroutineDispatcher>(),
+            mock<CoroutineScope>(),
             bgExecutor,
             shellInit,
             mockShellCommandHandler,
@@ -213,7 +218,8 @@
             mockTaskPositionerFactory,
             mockFocusTransitionObserver,
             desktopModeEventLogger,
-            mock<DesktopModeUiEventLogger>()
+            mock<DesktopModeUiEventLogger>(),
+            mock<WindowDecorTaskResourceLoader>()
         )
         desktopModeWindowDecorViewModel.setSplitScreenController(mockSplitScreenController)
         whenever(mockDisplayController.getDisplayLayout(any())).thenReturn(mockDisplayLayout)
@@ -293,8 +299,9 @@
         val decoration = Mockito.mock(DesktopModeWindowDecoration::class.java)
         whenever(
             mockDesktopModeWindowDecorFactory.create(
-                any(), any(), any(), any(), any(), any(), eq(task), any(), any(), any(), any(),
-                any(), any(), any(), any(), any(), any(), any(), any(), any())
+                any(), any(), any(), any(), any(), any(), any(), eq(task), any(), any(), any(),
+                any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(),
+                any(), any())
         ).thenReturn(decoration)
         decoration.mTaskInfo = task
         whenever(decoration.user).thenReturn(mockUserHandle)
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 db7b1f2..855b3dd 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
@@ -114,6 +114,7 @@
 import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
 import com.android.wm.shell.splitscreen.SplitScreenController;
 import com.android.wm.shell.windowdecor.WindowDecoration.RelayoutParams;
+import com.android.wm.shell.windowdecor.common.WindowDecorTaskResourceLoader;
 import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHost;
 import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHostSupplier;
 import com.android.wm.shell.windowdecor.viewholder.AppHeaderViewHolder;
@@ -122,6 +123,9 @@
 import kotlin.jvm.functions.Function0;
 import kotlin.jvm.functions.Function1;
 
+import kotlinx.coroutines.CoroutineScope;
+import kotlinx.coroutines.MainCoroutineDispatcher;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.BeforeClass;
@@ -173,6 +177,10 @@
     @Mock
     private Choreographer mMockChoreographer;
     @Mock
+    private MainCoroutineDispatcher mMockMainCoroutineDispatcher;
+    @Mock
+    private CoroutineScope mMockBgCoroutineScope;
+    @Mock
     private SyncTransactionQueue mMockSyncQueue;
     @Mock
     private AppHeaderViewHolder.Factory mMockAppHeaderViewHolderFactory;
@@ -224,6 +232,8 @@
     private DesktopModeEventLogger mDesktopModeEventLogger;
     @Mock
     private DesktopRepository mDesktopRepository;
+    @Mock
+    private WindowDecorTaskResourceLoader mMockTaskResourceLoader;
     @Captor
     private ArgumentCaptor<Function1<Boolean, Unit>> mOnMaxMenuHoverChangeListener;
     @Captor
@@ -234,6 +244,7 @@
     private StaticMockitoSession mMockitoSession;
     private TestableContext mTestableContext;
     private final ShellExecutor mBgExecutor = new TestShellExecutor();
+    private final ShellExecutor mMainExecutor = new TestShellExecutor();
     private final AssistContent mAssistContent = new AssistContent();
     private final Region mExclusionRegion = Region.obtain();
 
@@ -267,19 +278,19 @@
         when(mMockPackageManager.getApplicationLabel(any())).thenReturn("applicationLabel");
         final ActivityInfo activityInfo = createActivityInfo();
         when(mMockPackageManager.getActivityInfo(any(), anyInt())).thenReturn(activityInfo);
-        final ResolveInfo resolveInfo = new ResolveInfo();
-        resolveInfo.activityInfo = activityInfo;
-        when(mMockPackageManager.resolveActivity(any(), anyInt())).thenReturn(resolveInfo);
+        final ResolveInfo resolveInfo = createResolveInfo(false /* handleAllWebDataUri */);
+        when(mMockPackageManager.resolveActivityAsUser(any(), anyInt(), anyInt()))
+                .thenReturn(resolveInfo);
         final Display defaultDisplay = mock(Display.class);
         doReturn(defaultDisplay).when(mMockDisplayController).getDisplay(Display.DEFAULT_DISPLAY);
         doReturn(mInsetsState).when(mMockDisplayController).getInsetsState(anyInt());
-        when(mMockHandleMenuFactory.create(any(), any(), anyInt(), any(), any(), any(),
+        when(mMockHandleMenuFactory.create(any(), any(), any(), any(), any(), anyInt(), any(),
                 anyBoolean(), anyBoolean(), anyBoolean(), anyBoolean(), anyBoolean(), anyBoolean(),
                 any(), anyInt(), anyInt(), anyInt(), anyInt()))
                 .thenReturn(mMockHandleMenu);
         when(mMockMultiInstanceHelper.supportsMultiInstanceSplit(any())).thenReturn(false);
-        when(mMockAppHeaderViewHolderFactory.create(any(), any(), any(), any(), any(), any(), any(),
-                any())).thenReturn(mMockAppHeaderViewHolder);
+        when(mMockAppHeaderViewHolderFactory.create(any(), any(), any(), any(), any(), any()))
+                .thenReturn(mMockAppHeaderViewHolder);
         when(mMockDesktopUserRepositories.getCurrent()).thenReturn(mDesktopRepository);
         when(mMockDesktopUserRepositories.getProfile(anyInt())).thenReturn(mDesktopRepository);
         when(mMockWindowDecorViewHostSupplier.acquire(any(), eq(defaultDisplay)))
@@ -1653,11 +1664,9 @@
     @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_TO_WEB)
     public void browserApp_transferSessionUriUsedForBrowserAppWhenAvailable() {
         // Make {@link AppToWebUtils#isBrowserApp} return true
-        ResolveInfo resolveInfo = new ResolveInfo();
-        resolveInfo.handleAllWebDataURI = true;
-        resolveInfo.activityInfo = createActivityInfo();
+        ResolveInfo browserResolveInfo = createResolveInfo(true /* handleAllWebUriData */);
         when(mMockPackageManager.queryIntentActivitiesAsUser(any(), anyInt(), anyInt()))
-                .thenReturn(List.of(resolveInfo));
+                .thenReturn(List.of(browserResolveInfo));
 
         final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(true /* visible */);
         final DesktopModeWindowDecoration decor = createWindowDecoration(
@@ -1692,7 +1701,7 @@
 
 
     private void verifyHandleMenuCreated(@Nullable Uri uri) {
-        verify(mMockHandleMenuFactory).create(any(), any(), anyInt(), any(), any(),
+        verify(mMockHandleMenuFactory).create(any(), any(), any(), any(), any(), anyInt(),
                 any(), anyBoolean(), anyBoolean(), anyBoolean(), anyBoolean(), anyBoolean(),
                 anyBoolean(), argThat(intent ->
                         (uri == null && intent == null) || intent.getData().equals(uri)),
@@ -1760,12 +1769,14 @@
             MaximizeMenuFactory maximizeMenuFactory,
             boolean relayout) {
         final DesktopModeWindowDecoration windowDecor = new DesktopModeWindowDecoration(mContext,
-                mContext, mMockDisplayController, mMockSplitScreenController,
-                mMockDesktopUserRepositories, mMockShellTaskOrganizer, taskInfo,
-                mMockSurfaceControl, mMockHandler, mBgExecutor, mMockChoreographer, mMockSyncQueue,
-                mMockAppHeaderViewHolderFactory, mMockRootTaskDisplayAreaOrganizer,
-                mMockGenericLinksParser, mMockAssistContentRequester, SurfaceControl.Builder::new,
-                mMockTransactionSupplier, WindowContainerTransaction::new, SurfaceControl::new,
+                mContext, mMockDisplayController, mMockTaskResourceLoader,
+                mMockSplitScreenController, mMockDesktopUserRepositories, mMockShellTaskOrganizer,
+                taskInfo, mMockSurfaceControl, mMockHandler, mMainExecutor,
+                mMockMainCoroutineDispatcher, mMockBgCoroutineScope, mBgExecutor,
+                mMockChoreographer, mMockSyncQueue, mMockAppHeaderViewHolderFactory,
+                mMockRootTaskDisplayAreaOrganizer, mMockGenericLinksParser,
+                mMockAssistContentRequester, SurfaceControl.Builder::new, mMockTransactionSupplier,
+                WindowContainerTransaction::new, SurfaceControl::new,
                 new WindowManagerWrapper(mMockWindowManager), mMockSurfaceControlViewHostFactory,
                 mMockWindowDecorViewHostSupplier, maximizeMenuFactory, mMockHandleMenuFactory,
                 mMockMultiInstanceHelper, mMockCaptionHandleRepository, mDesktopModeEventLogger);
@@ -1780,6 +1791,13 @@
         return windowDecor;
     }
 
+    private ResolveInfo createResolveInfo(boolean handleAllWebDataURI) {
+        final ResolveInfo info = new ResolveInfo();
+        info.handleAllWebDataURI = handleAllWebDataURI;
+        info.activityInfo = createActivityInfo();
+        return info;
+    }
+
     private ActivityManager.RunningTaskInfo createTaskInfo(boolean visible) {
         final ActivityManager.TaskDescription.Builder taskDescriptionBuilder =
                 new ActivityManager.TaskDescription.Builder();
@@ -1808,6 +1826,7 @@
         applicationInfo.packageName = "DesktopModeWindowDecorationTestPackage";
         final ActivityInfo activityInfo = new ActivityInfo();
         activityInfo.applicationInfo = applicationInfo;
+        activityInfo.packageName = "DesktopModeWindowDecorationTestPackage";
         activityInfo.name = "DesktopModeWindowDecorationTest";
         return activityInfo;
     }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/FixedAspectRatioTaskPositionerDecoratorTests.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/FixedAspectRatioTaskPositionerDecoratorTests.kt
index ce17c1d..3c3d6b6 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/FixedAspectRatioTaskPositionerDecoratorTests.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/FixedAspectRatioTaskPositionerDecoratorTests.kt
@@ -68,9 +68,9 @@
             configuration.windowConfiguration.setBounds(PORTRAIT_BOUNDS)
         }
         doReturn(PORTRAIT_BOUNDS).`when`(mockTaskPositioner).onDragPositioningStart(
-            any(), any(), any())
-        doReturn(Rect()).`when`(mockTaskPositioner).onDragPositioningMove(any(), any())
-        doReturn(Rect()).`when`(mockTaskPositioner).onDragPositioningEnd(any(), any())
+            any(), any(), any(), any())
+        doReturn(Rect()).`when`(mockTaskPositioner).onDragPositioningMove(any(), any(), any())
+        doReturn(Rect()).`when`(mockTaskPositioner).onDragPositioningEnd(any(), any(), any())
         decoratedTaskPositioner = spy(
             FixedAspectRatioTaskPositionerDecorator(
             mockDesktopWindowDecoration, mockTaskPositioner)
@@ -87,7 +87,8 @@
             isResizeable = testCase.isResizeable
         }
 
-        decoratedTaskPositioner.onDragPositioningStart(testCase.ctrlType, originalX, originalY)
+        decoratedTaskPositioner.onDragPositioningStart(
+            testCase.ctrlType, DISPLAY_ID, originalX, originalY)
 
         val capturedValues = getLatestOnStartArguments()
         assertThat(capturedValues.ctrlType).isEqualTo(testCase.ctrlType)
@@ -102,7 +103,8 @@
         val originalX = 0f
         val originalY = 0f
 
-        decoratedTaskPositioner.onDragPositioningStart(testCase.ctrlType, originalX, originalY)
+        decoratedTaskPositioner.onDragPositioningStart(
+            testCase.ctrlType, DISPLAY_ID, originalX, originalY)
 
         val capturedValues = getLatestOnStartArguments()
         assertThat(capturedValues.ctrlType).isEqualTo(testCase.ctrlType)
@@ -119,7 +121,7 @@
             testCase.ctrlType, testCase.additionalEdgeCtrlType, startingBounds)
 
         decoratedTaskPositioner.onDragPositioningStart(
-            testCase.ctrlType, startingPoint.x, startingPoint.y)
+            testCase.ctrlType, DISPLAY_ID, startingPoint.x, startingPoint.y)
 
         val adjustedCtrlType = testCase.ctrlType + testCase.additionalEdgeCtrlType
         val capturedValues = getLatestOnStartArguments()
@@ -134,13 +136,14 @@
     ) {
         val originalX = 0f
         val originalY = 0f
-        decoratedTaskPositioner.onDragPositioningStart(testCase.ctrlType, originalX, originalX)
+        decoratedTaskPositioner.onDragPositioningStart(
+            testCase.ctrlType, DISPLAY_ID, originalX, originalX)
         mockDesktopWindowDecoration.mTaskInfo = ActivityManager.RunningTaskInfo().apply {
             isResizeable = testCase.isResizeable
         }
 
         decoratedTaskPositioner.onDragPositioningMove(
-            originalX + SMALL_DELTA, originalY + SMALL_DELTA)
+            DISPLAY_ID, originalX + SMALL_DELTA, originalY + SMALL_DELTA)
 
         val capturedValues = getLatestOnMoveArguments()
         assertThat(capturedValues.x).isEqualTo(originalX + SMALL_DELTA)
@@ -156,13 +159,14 @@
         val startingPoint = getCornerStartingPoint(testCase.ctrlType, startingBounds)
 
         decoratedTaskPositioner.onDragPositioningStart(
-            testCase.ctrlType, startingPoint.x, startingPoint.y)
+            testCase.ctrlType, DISPLAY_ID, startingPoint.x, startingPoint.y)
 
         val updatedBounds = decoratedTaskPositioner.onDragPositioningMove(
+            DISPLAY_ID,
             startingPoint.x + testCase.dragDelta.x,
             startingPoint.y + testCase.dragDelta.y)
 
-        verify(mockTaskPositioner, never()).onDragPositioningMove(any(), any())
+        verify(mockTaskPositioner, never()).onDragPositioningMove(any(), any(), any())
         assertThat(updatedBounds).isEqualTo(startingBounds)
     }
 
@@ -176,10 +180,12 @@
         val startingPoint = getCornerStartingPoint(testCase.ctrlType, startingBounds)
 
         decoratedTaskPositioner.onDragPositioningStart(
-            testCase.ctrlType, startingPoint.x, startingPoint.y)
+            testCase.ctrlType, DISPLAY_ID, startingPoint.x, startingPoint.y)
 
         decoratedTaskPositioner.onDragPositioningMove(
-            startingPoint.x + testCase.dragDelta.x, startingPoint.y + testCase.dragDelta.y)
+            DISPLAY_ID,
+            startingPoint.x + testCase.dragDelta.x,
+            startingPoint.y + testCase.dragDelta.y)
 
         val adjustedDragDelta = calculateAdjustedDelta(
             testCase.ctrlType, testCase.dragDelta, orientation)
@@ -202,9 +208,10 @@
             testCase.ctrlType, testCase.additionalEdgeCtrlType, startingBounds)
 
         decoratedTaskPositioner.onDragPositioningStart(
-            testCase.ctrlType, startingPoint.x, startingPoint.y)
+            testCase.ctrlType, DISPLAY_ID, startingPoint.x, startingPoint.y)
 
         decoratedTaskPositioner.onDragPositioningMove(
+            DISPLAY_ID,
             startingPoint.x + testCase.dragDelta.x,
             startingPoint.y + testCase.dragDelta.y)
 
@@ -227,13 +234,14 @@
     ) {
         val originalX = 0f
         val originalY = 0f
-        decoratedTaskPositioner.onDragPositioningStart(testCase.ctrlType, originalX, originalX)
+        decoratedTaskPositioner.onDragPositioningStart(testCase.ctrlType, DISPLAY_ID,
+            originalX, originalX)
         mockDesktopWindowDecoration.mTaskInfo = ActivityManager.RunningTaskInfo().apply {
             isResizeable = testCase.isResizeable
         }
 
         decoratedTaskPositioner.onDragPositioningEnd(
-            originalX + SMALL_DELTA, originalY + SMALL_DELTA)
+            DISPLAY_ID, originalX + SMALL_DELTA, originalY + SMALL_DELTA)
 
         val capturedValues = getLatestOnEndArguments()
         assertThat(capturedValues.x).isEqualTo(originalX + SMALL_DELTA)
@@ -249,9 +257,10 @@
         val startingPoint = getCornerStartingPoint(testCase.ctrlType, startingBounds)
 
         decoratedTaskPositioner.onDragPositioningStart(
-            testCase.ctrlType, startingPoint.x, startingPoint.y)
+            testCase.ctrlType, DISPLAY_ID, startingPoint.x, startingPoint.y)
 
         decoratedTaskPositioner.onDragPositioningEnd(
+            DISPLAY_ID,
             startingPoint.x + testCase.dragDelta.x,
             startingPoint.y + testCase.dragDelta.y)
 
@@ -269,10 +278,12 @@
         val startingPoint = getCornerStartingPoint(testCase.ctrlType, startingBounds)
 
         decoratedTaskPositioner.onDragPositioningStart(
-            testCase.ctrlType, startingPoint.x, startingPoint.y)
+            testCase.ctrlType, DISPLAY_ID, startingPoint.x, startingPoint.y)
 
         decoratedTaskPositioner.onDragPositioningEnd(
-            startingPoint.x + testCase.dragDelta.x, startingPoint.y + testCase.dragDelta.y)
+            DISPLAY_ID,
+            startingPoint.x + testCase.dragDelta.x,
+            startingPoint.y + testCase.dragDelta.y)
 
         val adjustedDragDelta = calculateAdjustedDelta(
             testCase.ctrlType, testCase.dragDelta, orientation)
@@ -295,9 +306,10 @@
             testCase.ctrlType, testCase.additionalEdgeCtrlType, startingBounds)
 
         decoratedTaskPositioner.onDragPositioningStart(
-            testCase.ctrlType, startingPoint.x, startingPoint.y)
+            testCase.ctrlType, DISPLAY_ID, startingPoint.x, startingPoint.y)
 
         decoratedTaskPositioner.onDragPositioningEnd(
+            DISPLAY_ID,
             startingPoint.x + testCase.dragDelta.x,
             startingPoint.y + testCase.dragDelta.y)
 
@@ -322,7 +334,7 @@
         val captorCtrlType = argumentCaptor<Int>()
         val captorCoordinates = argumentCaptor<Float>()
         verify(mockTaskPositioner).onDragPositioningStart(
-            captorCtrlType.capture(), captorCoordinates.capture(), captorCoordinates.capture())
+            captorCtrlType.capture(), any(), captorCoordinates.capture(), captorCoordinates.capture())
 
         return CtrlCoordinateCapture(captorCtrlType.firstValue, captorCoordinates.firstValue,
             captorCoordinates.secondValue)
@@ -335,7 +347,7 @@
     private fun getLatestOnMoveArguments(): PointF {
         val captorCoordinates = argumentCaptor<Float>()
         verify(mockTaskPositioner).onDragPositioningMove(
-            captorCoordinates.capture(), captorCoordinates.capture())
+            any(), captorCoordinates.capture(), captorCoordinates.capture())
 
         return PointF(captorCoordinates.firstValue, captorCoordinates.secondValue)
     }
@@ -347,7 +359,7 @@
     private fun getLatestOnEndArguments(): PointF {
         val captorCoordinates = argumentCaptor<Float>()
         verify(mockTaskPositioner).onDragPositioningEnd(
-            captorCoordinates.capture(), captorCoordinates.capture())
+            any(), captorCoordinates.capture(), captorCoordinates.capture())
 
         return PointF(captorCoordinates.firstValue, captorCoordinates.secondValue)
     }
@@ -358,7 +370,7 @@
     private fun getAndMockBounds(orientation: Orientation): Rect {
         val mockBounds = if (orientation.isPortrait) PORTRAIT_BOUNDS else LANDSCAPE_BOUNDS
         doReturn(mockBounds).`when`(mockTaskPositioner).onDragPositioningStart(
-            any(), any(), any())
+            any(), any(), any(), any())
         doReturn(mockBounds).`when`(decoratedTaskPositioner).getBounds(any())
         return mockBounds
     }
@@ -458,6 +470,7 @@
         private val STARTING_ASPECT_RATIO = PORTRAIT_BOUNDS.height() / PORTRAIT_BOUNDS.width()
         private const val LARGE_DELTA = 50f
         private const val SMALL_DELTA = 30f
+        private const val DISPLAY_ID = 1
 
         enum class Orientation(
             val isPortrait: Boolean
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositionerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositionerTest.kt
index 3b80cb4..cec5251 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositionerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositionerTest.kt
@@ -150,11 +150,13 @@
     fun testDragResize_notMove_skipsTransitionOnEnd() {
         taskPositioner.onDragPositioningStart(
                 CTRL_TYPE_TOP or CTRL_TYPE_RIGHT,
+                DISPLAY_ID,
                 STARTING_BOUNDS.left.toFloat(),
                 STARTING_BOUNDS.top.toFloat()
         )
 
         taskPositioner.onDragPositioningEnd(
+                DISPLAY_ID,
                 STARTING_BOUNDS.left.toFloat() + 10,
                 STARTING_BOUNDS.top.toFloat() + 10
         )
@@ -171,11 +173,13 @@
     fun testDragResize_noEffectiveMove_skipsTransitionOnMoveAndEnd() {
         taskPositioner.onDragPositioningStart(
                 CTRL_TYPE_TOP or CTRL_TYPE_RIGHT,
+                DISPLAY_ID,
                 STARTING_BOUNDS.left.toFloat(),
                 STARTING_BOUNDS.top.toFloat()
         )
 
         taskPositioner.onDragPositioningMove(
+                DISPLAY_ID,
                 STARTING_BOUNDS.left.toFloat(),
                 STARTING_BOUNDS.top.toFloat()
         )
@@ -188,6 +192,7 @@
         })
 
         taskPositioner.onDragPositioningEnd(
+                DISPLAY_ID,
                 STARTING_BOUNDS.left.toFloat() + 10,
                 STARTING_BOUNDS.top.toFloat() + 10
         )
@@ -204,11 +209,13 @@
     fun testDragResize_hasEffectiveMove_issuesTransitionOnMoveAndEnd() {
         taskPositioner.onDragPositioningStart(
                 CTRL_TYPE_TOP or CTRL_TYPE_RIGHT,
+                DISPLAY_ID,
                 STARTING_BOUNDS.left.toFloat(),
                 STARTING_BOUNDS.top.toFloat()
         )
 
         taskPositioner.onDragPositioningMove(
+                DISPLAY_ID,
                 STARTING_BOUNDS.left.toFloat() + 10,
                 STARTING_BOUNDS.top.toFloat()
         )
@@ -224,6 +231,7 @@
         verify(mockDragEventListener, times(1)).onDragMove(eq(TASK_ID))
 
         taskPositioner.onDragPositioningEnd(
+                DISPLAY_ID,
                 STARTING_BOUNDS.left.toFloat() + 10,
                 STARTING_BOUNDS.top.toFloat() + 10
         )
@@ -242,6 +250,7 @@
     fun testDragResize_move_skipsDragResizingFlag() {
         taskPositioner.onDragPositioningStart(
                 CTRL_TYPE_UNDEFINED, // Move
+                DISPLAY_ID,
                 STARTING_BOUNDS.left.toFloat(),
                 STARTING_BOUNDS.top.toFloat()
         )
@@ -250,11 +259,12 @@
         val newX = STARTING_BOUNDS.left.toFloat() + 10
         val newY = STARTING_BOUNDS.top.toFloat()
         taskPositioner.onDragPositioningMove(
+                DISPLAY_ID,
                 newX,
                 newY
         )
 
-        taskPositioner.onDragPositioningEnd(newX, newY)
+        taskPositioner.onDragPositioningEnd(DISPLAY_ID, newX, newY)
 
         verify(mockShellTaskOrganizer, never()).applyTransaction(argThat { wct ->
             return@argThat wct.changes.any { (token, change) ->
@@ -276,6 +286,7 @@
     fun testDragResize_resize_setsDragResizingFlag() {
         taskPositioner.onDragPositioningStart(
                 CTRL_TYPE_RIGHT, // Resize right
+                DISPLAY_ID,
                 STARTING_BOUNDS.left.toFloat(),
                 STARTING_BOUNDS.top.toFloat()
         )
@@ -284,11 +295,12 @@
         val newX = STARTING_BOUNDS.right.toFloat() + 10
         val newY = STARTING_BOUNDS.top.toFloat()
         taskPositioner.onDragPositioningMove(
+                DISPLAY_ID,
                 newX,
                 newY
         )
 
-        taskPositioner.onDragPositioningEnd(newX, newY)
+        taskPositioner.onDragPositioningEnd(DISPLAY_ID, newX, newY)
 
         verify(mockShellTaskOrganizer).applyTransaction(argThat { wct ->
             return@argThat wct.changes.any { (token, change) ->
@@ -310,6 +322,7 @@
     fun testDragResize_resize_setBoundsDoesNotChangeHeightWhenLessThanMin() {
         taskPositioner.onDragPositioningStart(
                 CTRL_TYPE_RIGHT or CTRL_TYPE_TOP, // Resize right and top
+                DISPLAY_ID,
                 STARTING_BOUNDS.right.toFloat(),
                 STARTING_BOUNDS.top.toFloat()
         )
@@ -318,11 +331,12 @@
         val newX = STARTING_BOUNDS.right.toFloat() - 5
         val newY = STARTING_BOUNDS.top.toFloat() + 95
         taskPositioner.onDragPositioningMove(
+                DISPLAY_ID,
                 newX,
                 newY
         )
 
-        taskPositioner.onDragPositioningEnd(newX, newY)
+        taskPositioner.onDragPositioningEnd(DISPLAY_ID, newX, newY)
 
         verify(mockShellTaskOrganizer).applyTransaction(argThat { wct ->
             return@argThat wct.changes.any { (token, change) ->
@@ -340,6 +354,7 @@
     fun testDragResize_resize_setBoundsDoesNotChangeWidthWhenLessThanMin() {
         taskPositioner.onDragPositioningStart(
                 CTRL_TYPE_RIGHT or CTRL_TYPE_TOP, // Resize right and top
+                DISPLAY_ID,
                 STARTING_BOUNDS.right.toFloat(),
                 STARTING_BOUNDS.top.toFloat()
         )
@@ -348,11 +363,12 @@
         val newX = STARTING_BOUNDS.right.toFloat() - 95
         val newY = STARTING_BOUNDS.top.toFloat() + 5
         taskPositioner.onDragPositioningMove(
+                DISPLAY_ID,
                 newX,
                 newY
         )
 
-        taskPositioner.onDragPositioningEnd(newX, newY)
+        taskPositioner.onDragPositioningEnd(DISPLAY_ID, newX, newY)
 
         verify(mockShellTaskOrganizer).applyTransaction(argThat { wct ->
             return@argThat wct.changes.any { (token, change) ->
@@ -370,6 +386,7 @@
     fun testDragResize_resize_setBoundsDoesNotChangeHeightWhenNegative() {
         taskPositioner.onDragPositioningStart(
                 CTRL_TYPE_RIGHT or CTRL_TYPE_TOP, // Resize right and top
+                DISPLAY_ID,
                 STARTING_BOUNDS.right.toFloat(),
                 STARTING_BOUNDS.top.toFloat()
         )
@@ -378,11 +395,12 @@
         val newX = STARTING_BOUNDS.right.toFloat() - 5
         val newY = STARTING_BOUNDS.top.toFloat() + 105
         taskPositioner.onDragPositioningMove(
+                DISPLAY_ID,
                 newX,
                 newY
         )
 
-        taskPositioner.onDragPositioningEnd(newX, newY)
+        taskPositioner.onDragPositioningEnd(DISPLAY_ID, newX, newY)
 
         verify(mockShellTaskOrganizer).applyTransaction(argThat { wct ->
             return@argThat wct.changes.any { (token, change) ->
@@ -400,6 +418,7 @@
     fun testDragResize_resize_setBoundsDoesNotChangeWidthWhenNegative() {
         taskPositioner.onDragPositioningStart(
                 CTRL_TYPE_RIGHT or CTRL_TYPE_TOP, // Resize right and top
+                DISPLAY_ID,
                 STARTING_BOUNDS.right.toFloat(),
                 STARTING_BOUNDS.top.toFloat()
         )
@@ -408,11 +427,12 @@
         val newX = STARTING_BOUNDS.right.toFloat() - 105
         val newY = STARTING_BOUNDS.top.toFloat() + 5
         taskPositioner.onDragPositioningMove(
+                DISPLAY_ID,
                 newX,
                 newY
         )
 
-        taskPositioner.onDragPositioningEnd(newX, newY)
+        taskPositioner.onDragPositioningEnd(DISPLAY_ID, newX, newY)
 
         verify(mockShellTaskOrganizer).applyTransaction(argThat { wct ->
             return@argThat wct.changes.any { (token, change) ->
@@ -430,6 +450,7 @@
     fun testDragResize_resize_setBoundsRunsWhenResizeBoundsValid() {
         taskPositioner.onDragPositioningStart(
                 CTRL_TYPE_RIGHT or CTRL_TYPE_TOP, // Resize right and top
+                DISPLAY_ID,
                 STARTING_BOUNDS.right.toFloat(),
                 STARTING_BOUNDS.top.toFloat()
         )
@@ -438,11 +459,12 @@
         val newX = STARTING_BOUNDS.right.toFloat() - 80
         val newY = STARTING_BOUNDS.top.toFloat() + 80
         taskPositioner.onDragPositioningMove(
+                DISPLAY_ID,
                 newX,
                 newY
         )
 
-        taskPositioner.onDragPositioningEnd(newX, newY)
+        taskPositioner.onDragPositioningEnd(DISPLAY_ID, newX, newY)
 
         verify(mockShellTaskOrganizer).applyTransaction(argThat { wct ->
             return@argThat wct.changes.any { (token, change) ->
@@ -456,6 +478,7 @@
     fun testDragResize_resize_setBoundsDoesNotRunWithNegativeHeightAndWidth() {
         taskPositioner.onDragPositioningStart(
                 CTRL_TYPE_RIGHT or CTRL_TYPE_TOP, // Resize right and top
+                DISPLAY_ID,
                 STARTING_BOUNDS.right.toFloat(),
                 STARTING_BOUNDS.top.toFloat()
         )
@@ -464,11 +487,12 @@
         val newX = STARTING_BOUNDS.right.toFloat() - 95
         val newY = STARTING_BOUNDS.top.toFloat() + 95
         taskPositioner.onDragPositioningMove(
+                DISPLAY_ID,
                 newX,
                 newY
         )
 
-        taskPositioner.onDragPositioningEnd(newX, newY)
+        taskPositioner.onDragPositioningEnd(DISPLAY_ID, newX, newY)
 
         verify(mockShellTaskOrganizer, never()).applyTransaction(argThat { wct ->
             return@argThat wct.changes.any { (token, change) ->
@@ -484,6 +508,7 @@
 
         taskPositioner.onDragPositioningStart(
                 CTRL_TYPE_RIGHT or CTRL_TYPE_TOP, // Resize right and top
+                DISPLAY_ID,
                 STARTING_BOUNDS.right.toFloat(),
                 STARTING_BOUNDS.top.toFloat()
         )
@@ -492,11 +517,12 @@
         val newX = STARTING_BOUNDS.right.toFloat() - 97
         val newY = STARTING_BOUNDS.top.toFloat() + 97
         taskPositioner.onDragPositioningMove(
+                DISPLAY_ID,
                 newX,
                 newY
         )
 
-        taskPositioner.onDragPositioningEnd(newX, newY)
+        taskPositioner.onDragPositioningEnd(DISPLAY_ID, newX, newY)
 
         verify(mockShellTaskOrganizer, never()).applyTransaction(argThat { wct ->
             return@argThat wct.changes.any { (token, change) ->
@@ -510,6 +536,7 @@
     fun testDragResize_resize_useMinWidthWhenValid() {
         taskPositioner.onDragPositioningStart(
                 CTRL_TYPE_RIGHT or CTRL_TYPE_TOP, // Resize right and top
+                DISPLAY_ID,
                 STARTING_BOUNDS.right.toFloat(),
                 STARTING_BOUNDS.top.toFloat()
         )
@@ -518,11 +545,12 @@
         val newX = STARTING_BOUNDS.right.toFloat() - 93
         val newY = STARTING_BOUNDS.top.toFloat() + 93
         taskPositioner.onDragPositioningMove(
+                DISPLAY_ID,
                 newX,
                 newY
         )
 
-        taskPositioner.onDragPositioningEnd(newX, newY)
+        taskPositioner.onDragPositioningEnd(DISPLAY_ID, newX, newY)
 
         verify(mockShellTaskOrganizer, never()).applyTransaction(argThat { wct ->
             return@argThat wct.changes.any { (token, change) ->
@@ -535,6 +563,7 @@
     fun testDragResize_toDisallowedBounds_freezesAtLimit() {
         taskPositioner.onDragPositioningStart(
                 CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM, // Resize right-bottom corner
+                DISPLAY_ID,
                 STARTING_BOUNDS.right.toFloat(),
                 STARTING_BOUNDS.bottom.toFloat()
         )
@@ -546,6 +575,7 @@
                 STARTING_BOUNDS.right + 10,
                 STARTING_BOUNDS.bottom + 10)
         taskPositioner.onDragPositioningMove(
+                DISPLAY_ID,
                 newBounds.right.toFloat(),
                 newBounds.bottom.toFloat()
         )
@@ -559,11 +589,13 @@
                 DISALLOWED_RESIZE_AREA.top
         )
         taskPositioner.onDragPositioningMove(
+                DISPLAY_ID,
                 newBounds2.right.toFloat(),
                 newBounds2.bottom.toFloat()
         )
 
-        taskPositioner.onDragPositioningEnd(newBounds2.right.toFloat(), newBounds2.bottom.toFloat())
+        taskPositioner.onDragPositioningEnd(DISPLAY_ID, newBounds2.right.toFloat(),
+            newBounds2.bottom.toFloat())
 
         // The first resize falls in the allowed area, verify there's a change for it.
         verify(mockShellTaskOrganizer).applyTransaction(argThat { wct ->
@@ -629,6 +661,7 @@
         mockWindowDecoration.mHasGlobalFocus = false
         taskPositioner.onDragPositioningStart(
                 CTRL_TYPE_RIGHT, // Resize right
+                DISPLAY_ID,
                 STARTING_BOUNDS.left.toFloat(),
                 STARTING_BOUNDS.top.toFloat()
         )
@@ -645,6 +678,7 @@
         mockWindowDecoration.mHasGlobalFocus = true
         taskPositioner.onDragPositioningStart(
                 CTRL_TYPE_RIGHT, // Resize right
+                DISPLAY_ID,
                 STARTING_BOUNDS.left.toFloat(),
                 STARTING_BOUNDS.top.toFloat()
         )
@@ -661,6 +695,7 @@
         mockWindowDecoration.mHasGlobalFocus = false
         taskPositioner.onDragPositioningStart(
                 CTRL_TYPE_UNDEFINED, // drag
+                DISPLAY_ID,
                 STARTING_BOUNDS.left.toFloat(),
                 STARTING_BOUNDS.top.toFloat()
         )
@@ -729,11 +764,13 @@
 
         taskPositioner.onDragPositioningStart(
                 CTRL_TYPE_TOP or CTRL_TYPE_RIGHT,
+                DISPLAY_ID,
                 STARTING_BOUNDS.left.toFloat(),
                 STARTING_BOUNDS.top.toFloat()
         )
 
         taskPositioner.onDragPositioningMove(
+                DISPLAY_ID,
                 STARTING_BOUNDS.left.toFloat() - 20,
                 STARTING_BOUNDS.top.toFloat() - 20
         )
@@ -742,6 +779,7 @@
         assertTrue(taskPositioner.isResizingOrAnimating)
 
         taskPositioner.onDragPositioningEnd(
+                DISPLAY_ID,
                 STARTING_BOUNDS.left.toFloat(),
                 STARTING_BOUNDS.top.toFloat()
         )
@@ -785,15 +823,18 @@
     ) {
         taskPositioner.onDragPositioningStart(
             ctrlType,
+            DISPLAY_ID,
             startX,
             startY
         )
         taskPositioner.onDragPositioningMove(
+            DISPLAY_ID,
             endX,
             endY
         )
 
         taskPositioner.onDragPositioningEnd(
+            DISPLAY_ID,
             endX,
             endY
         )
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt
index 3bcbcbdd..cbfb57e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt
@@ -24,6 +24,7 @@
 import android.graphics.Color
 import android.graphics.Point
 import android.graphics.Rect
+import android.graphics.drawable.BitmapDrawable
 import android.platform.test.annotations.EnableFlags
 import android.platform.test.flag.junit.SetFlagsRule
 import android.testing.AndroidTestingRunner
@@ -49,6 +50,13 @@
 import com.android.wm.shell.splitscreen.SplitScreenController
 import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalSystemViewContainer
 import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalViewHostViewContainer
+import com.android.wm.shell.windowdecor.common.WindowDecorTaskResourceLoader
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.advanceUntilIdle
+import kotlinx.coroutines.test.runTest
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertTrue
 import org.junit.Before
@@ -68,6 +76,7 @@
  * Build/Install/Run:
  * atest WMShellUnitTests:HandleMenuTest
  */
+@OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
 @TestableLooper.RunWithLooper
 @RunWith(AndroidTestingRunner::class)
@@ -81,14 +90,6 @@
     @Mock
     private lateinit var mockWindowManager: WindowManager
     @Mock
-    private lateinit var onClickListener: View.OnClickListener
-    @Mock
-    private lateinit var onTouchListener: View.OnTouchListener
-    @Mock
-    private lateinit var appIcon: Bitmap
-    @Mock
-    private lateinit var appName: CharSequence
-    @Mock
     private lateinit var displayController: DisplayController
     @Mock
     private lateinit var splitScreenController: SplitScreenController
@@ -96,6 +97,10 @@
     private lateinit var displayLayout: DisplayLayout
     @Mock
     private lateinit var mockSurfaceControlViewHost: SurfaceControlViewHost
+    @Mock
+    private lateinit var mockTaskResourceLoader: WindowDecorTaskResourceLoader
+    @Mock
+    private lateinit var mockAppIcon: Bitmap
 
     private lateinit var handleMenu: HandleMenu
 
@@ -136,7 +141,7 @@
 
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_HANDLE_INPUT_FIX)
-    fun testFullscreenMenuUsesSystemViewContainer() {
+    fun testFullscreenMenuUsesSystemViewContainer() = runTest {
         createTaskInfo(WINDOWING_MODE_FULLSCREEN, SPLIT_POSITION_UNDEFINED)
         val handleMenu = createAndShowHandleMenu(SPLIT_POSITION_UNDEFINED)
         assertTrue(handleMenu.handleMenuViewContainer is AdditionalSystemViewContainer)
@@ -148,7 +153,7 @@
 
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_HANDLE_INPUT_FIX)
-    fun testFreeformMenu_usesViewHostViewContainer() {
+    fun testFreeformMenu_usesViewHostViewContainer() = runTest {
         createTaskInfo(WINDOWING_MODE_FREEFORM, SPLIT_POSITION_UNDEFINED)
         handleMenu = createAndShowHandleMenu(SPLIT_POSITION_UNDEFINED)
         assertTrue(handleMenu.handleMenuViewContainer is AdditionalViewHostViewContainer)
@@ -159,7 +164,7 @@
 
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_HANDLE_INPUT_FIX)
-    fun testSplitLeftMenu_usesSystemViewContainer() {
+    fun testSplitLeftMenu_usesSystemViewContainer() = runTest {
         createTaskInfo(WINDOWING_MODE_MULTI_WINDOW, SPLIT_POSITION_TOP_OR_LEFT)
         handleMenu = createAndShowHandleMenu(SPLIT_POSITION_TOP_OR_LEFT)
         assertTrue(handleMenu.handleMenuViewContainer is AdditionalSystemViewContainer)
@@ -174,7 +179,7 @@
 
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_HANDLE_INPUT_FIX)
-    fun testSplitRightMenu_usesSystemViewContainer() {
+    fun testSplitRightMenu_usesSystemViewContainer() = runTest {
         createTaskInfo(WINDOWING_MODE_MULTI_WINDOW, SPLIT_POSITION_BOTTOM_OR_RIGHT)
         handleMenu = createAndShowHandleMenu(SPLIT_POSITION_BOTTOM_OR_RIGHT)
         assertTrue(handleMenu.handleMenuViewContainer is AdditionalSystemViewContainer)
@@ -188,7 +193,7 @@
     }
 
     @Test
-    fun testCreate_forceShowSystemBars_usesSystemViewContainer() {
+    fun testCreate_forceShowSystemBars_usesSystemViewContainer() = runTest {
         createTaskInfo(WINDOWING_MODE_FREEFORM)
 
         handleMenu = createAndShowHandleMenu(forceShowSystemBars = true)
@@ -198,7 +203,7 @@
     }
 
     @Test
-    fun testCreate_forceShowSystemBars() {
+    fun testCreate_forceShowSystemBars() = runTest {
         createTaskInfo(WINDOWING_MODE_FREEFORM)
 
         handleMenu = createAndShowHandleMenu(forceShowSystemBars = true)
@@ -208,6 +213,18 @@
         assertTrue((types and systemBars()) != 0)
     }
 
+    @Test
+    fun testCreate_loadsAppInfoInBackground() = runTest {
+        createTaskInfo(WINDOWING_MODE_FREEFORM)
+
+        handleMenu = createAndShowHandleMenu()
+        advanceUntilIdle()
+
+        assertThat(handleMenu.handleMenuView!!.appNameView.text).isEqualTo(APP_NAME)
+        val drawable = handleMenu.handleMenuView!!.appIconView.drawable as BitmapDrawable
+        assertThat(drawable.bitmap).isEqualTo(mockAppIcon)
+    }
+
     private fun createTaskInfo(windowingMode: Int, splitPosition: Int? = null) {
         val taskDescriptionBuilder = ActivityManager.TaskDescription.Builder()
             .setBackgroundColor(Color.YELLOW)
@@ -238,9 +255,13 @@
                 (it.arguments[1] as Rect).set(SPLIT_RIGHT_BOUNDS)
             }
         }
+        whenever(mockTaskResourceLoader.getName(mockDesktopWindowDecoration.mTaskInfo))
+            .thenReturn(APP_NAME)
+        whenever(mockTaskResourceLoader.getHeaderIcon(mockDesktopWindowDecoration.mTaskInfo))
+            .thenReturn(mockAppIcon)
     }
 
-    private fun createAndShowHandleMenu(
+    private fun TestScope.createAndShowHandleMenu(
         splitPosition: Int? = null,
         forceShowSystemBars: Boolean = false
     ): HandleMenu {
@@ -262,12 +283,22 @@
             }
             else -> error("Invalid windowing mode")
         }
-        val handleMenu = HandleMenu(mockDesktopWindowDecoration,
+        val handleMenu = HandleMenu(
+            StandardTestDispatcher(testScheduler),
+            this,
+            mockDesktopWindowDecoration,
             WindowManagerWrapper(mockWindowManager),
-            layoutId, appIcon, appName, splitScreenController, shouldShowWindowingPill = true,
-            shouldShowNewWindowButton = true, shouldShowManageWindowsButton = false,
-            shouldShowChangeAspectRatioButton = false, shouldShowDesktopModeButton = true,
-            isBrowserApp = false, null /* openInAppOrBrowserIntent */, captionWidth = HANDLE_WIDTH,
+            mockTaskResourceLoader,
+            layoutId,
+            splitScreenController,
+            shouldShowWindowingPill = true,
+            shouldShowNewWindowButton = true,
+            shouldShowManageWindowsButton = false,
+            shouldShowChangeAspectRatioButton = false,
+            shouldShowDesktopModeButton = true,
+            isBrowserApp = false,
+            null /* openInAppOrBrowserIntent */,
+            captionWidth = HANDLE_WIDTH,
             captionHeight = 50,
             captionX = captionX,
             captionY = 0,
@@ -300,5 +331,6 @@
         private const val MENU_PILL_ELEVATION = 2
         private const val MENU_PILL_SPACING_MARGIN = 4
         private const val HANDLE_WIDTH = 80
+        private const val APP_NAME = "Test App"
     }
 }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/ResizeVeilTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/ResizeVeilTest.kt
index e0d16aa..fa3d3e4 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/ResizeVeilTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/ResizeVeilTest.kt
@@ -17,6 +17,7 @@
 
 import android.graphics.Bitmap
 import android.graphics.Rect
+import android.graphics.drawable.BitmapDrawable
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import android.view.Display
@@ -29,6 +30,13 @@
 import com.android.wm.shell.common.DisplayController
 import com.android.wm.shell.common.DisplayController.OnDisplaysChangedListener
 import com.android.wm.shell.windowdecor.WindowDecoration.SurfaceControlViewHostFactory
+import com.android.wm.shell.windowdecor.common.WindowDecorTaskResourceLoader
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.advanceUntilIdle
+import kotlinx.coroutines.test.runTest
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -54,6 +62,7 @@
  * Build/Install/Run:
  * atest WMShellUnitTests:ResizeVeilTest
  */
+@OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
 @RunWith(AndroidTestingRunner::class)
 @TestableLooper.RunWithLooper
@@ -85,6 +94,8 @@
     private lateinit var mockIconSurface: SurfaceControl
     @Mock
     private lateinit var mockTransaction: SurfaceControl.Transaction
+    @Mock
+    private lateinit var mockTaskResourceLoader: WindowDecorTaskResourceLoader
 
     private val taskInfo = TestRunningTaskInfoBuilder().build()
 
@@ -115,7 +126,7 @@
     }
 
     @Test
-    fun init_displayAvailable_viewHostCreated() {
+    fun init_displayAvailable_viewHostCreated() = runTest {
         createResizeVeil(withDisplayAvailable = true)
 
         verify(mockSurfaceControlViewHostFactory)
@@ -123,7 +134,7 @@
     }
 
     @Test
-    fun init_displayUnavailable_viewHostNotCreatedUntilDisplayAppears() {
+    fun init_displayUnavailable_viewHostNotCreatedUntilDisplayAppears() = runTest {
         createResizeVeil(withDisplayAvailable = false)
 
         verify(mockSurfaceControlViewHostFactory, never())
@@ -140,14 +151,14 @@
     }
 
     @Test
-    fun dispose_removesDisplayWindowListener() {
+    fun dispose_removesDisplayWindowListener() = runTest {
         createResizeVeil().dispose()
 
         verify(mockDisplayController).removeDisplayWindowListener(any())
     }
 
     @Test
-    fun showVeil() {
+    fun showVeil() = runTest {
         val veil = createResizeVeil()
 
         veil.showVeil(mockTransaction, mock(), Rect(0, 0, 100, 100), taskInfo, false /* fadeIn */)
@@ -159,7 +170,7 @@
     }
 
     @Test
-    fun showVeil_displayUnavailable_doesNotShow() {
+    fun showVeil_displayUnavailable_doesNotShow() = runTest {
         val veil = createResizeVeil(withDisplayAvailable = false)
 
         veil.showVeil(mockTransaction, mock(), Rect(0, 0, 100, 100), taskInfo, false /* fadeIn */)
@@ -171,7 +182,7 @@
     }
 
     @Test
-    fun showVeil_alreadyVisible_doesNotShowAgain() {
+    fun showVeil_alreadyVisible_doesNotShowAgain() = runTest {
         val veil = createResizeVeil()
 
         veil.showVeil(mockTransaction, mock(), Rect(0, 0, 100, 100), taskInfo, false /* fadeIn */)
@@ -184,7 +195,7 @@
     }
 
     @Test
-    fun showVeil_reparentsVeilToNewParent() {
+    fun showVeil_reparentsVeilToNewParent() = runTest {
         val veil = createResizeVeil(parent = mock())
 
         val newParent = mock<SurfaceControl>()
@@ -200,7 +211,7 @@
     }
 
     @Test
-    fun hideVeil_alreadyHidden_doesNothing() {
+    fun hideVeil_alreadyHidden_doesNothing() = runTest {
         val veil = createResizeVeil()
 
         veil.hideVeil()
@@ -208,16 +219,41 @@
         verifyZeroInteractions(mockTransaction)
     }
 
-    private fun createResizeVeil(
+    @Test
+    fun showVeil_loadsIconInBackground() = runTest {
+        val veil = createResizeVeil()
+        veil.showVeil(mockTransaction, mock(), Rect(0, 0, 100, 100), taskInfo, false /* fadeIn */)
+
+        advanceUntilIdle()
+
+        verify(mockTaskResourceLoader).getVeilIcon(taskInfo)
+        assertThat((veil.iconView.drawable as BitmapDrawable).bitmap).isEqualTo(mockAppIcon)
+    }
+
+    @Test
+    fun dispose_iconLoading_cancelsJob() = runTest {
+        val veil = createResizeVeil()
+        veil.showVeil(mockTransaction, mock(), Rect(0, 0, 100, 100), taskInfo, false /* fadeIn */)
+
+        veil.dispose()
+        advanceUntilIdle()
+
+        assertThat(veil.iconView.drawable).isNull()
+    }
+
+    private fun TestScope.createResizeVeil(
         withDisplayAvailable: Boolean = true,
         parent: SurfaceControl = mock()
     ): ResizeVeil {
         whenever(mockDisplayController.getDisplay(taskInfo.displayId))
             .thenReturn(if (withDisplayAvailable) mockDisplay else null)
+        whenever(mockTaskResourceLoader.getVeilIcon(taskInfo)).thenReturn(mockAppIcon)
         return ResizeVeil(
             context,
             mockDisplayController,
-            mockAppIcon,
+            mockTaskResourceLoader,
+            StandardTestDispatcher(testScheduler),
+            this,
             parent,
             { mockTransaction },
             mockSurfaceControlBuilderFactory,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositionerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositionerTest.kt
index e7df864..eb8c0dd 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositionerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositionerTest.kt
@@ -168,12 +168,14 @@
     fun testDragResize_noMove_doesNotShowResizeVeil() = runOnUiThread {
         taskPositioner.onDragPositioningStart(
             CTRL_TYPE_TOP or CTRL_TYPE_RIGHT,
+            DISPLAY_ID,
             STARTING_BOUNDS.left.toFloat(),
             STARTING_BOUNDS.top.toFloat()
         )
         verify(mockDesktopWindowDecoration, never()).showResizeVeil(STARTING_BOUNDS)
 
         taskPositioner.onDragPositioningEnd(
+            DISPLAY_ID,
             STARTING_BOUNDS.left.toFloat(),
             STARTING_BOUNDS.top.toFloat()
         )
@@ -191,11 +193,13 @@
     fun testDragResize_movesTask_doesNotShowResizeVeil() = runOnUiThread {
         taskPositioner.onDragPositioningStart(
             CTRL_TYPE_UNDEFINED,
+            DISPLAY_ID,
             STARTING_BOUNDS.left.toFloat(),
             STARTING_BOUNDS.top.toFloat()
         )
 
         taskPositioner.onDragPositioningMove(
+            DISPLAY_ID,
             STARTING_BOUNDS.left.toFloat() + 60,
             STARTING_BOUNDS.top.toFloat() + 100
         )
@@ -208,6 +212,7 @@
                 eq(rectAfterMove.top.toFloat()))
 
         val endBounds = taskPositioner.onDragPositioningEnd(
+            DISPLAY_ID,
             STARTING_BOUNDS.left.toFloat() + 70,
             STARTING_BOUNDS.top.toFloat() + 20
         )
@@ -226,11 +231,13 @@
     fun testDragResize_resize_boundsUpdateOnEnd() = runOnUiThread {
         taskPositioner.onDragPositioningStart(
             CTRL_TYPE_RIGHT or CTRL_TYPE_TOP,
+            DISPLAY_ID,
             STARTING_BOUNDS.right.toFloat(),
             STARTING_BOUNDS.top.toFloat()
         )
 
         taskPositioner.onDragPositioningMove(
+            DISPLAY_ID,
             STARTING_BOUNDS.right.toFloat() + 10,
             STARTING_BOUNDS.top.toFloat() + 10
         )
@@ -248,6 +255,7 @@
         })
 
         taskPositioner.onDragPositioningEnd(
+            DISPLAY_ID,
             STARTING_BOUNDS.right.toFloat() + 20,
             STARTING_BOUNDS.top.toFloat() + 20
         )
@@ -266,17 +274,20 @@
     @Test
     fun testDragResize_noEffectiveMove_skipsTransactionOnEnd() = runOnUiThread {
         taskPositioner.onDragPositioningStart(
+            DISPLAY_ID,
             CTRL_TYPE_TOP or CTRL_TYPE_RIGHT,
             STARTING_BOUNDS.left.toFloat(),
             STARTING_BOUNDS.top.toFloat()
         )
 
         taskPositioner.onDragPositioningMove(
+            DISPLAY_ID,
             STARTING_BOUNDS.left.toFloat(),
             STARTING_BOUNDS.top.toFloat()
         )
 
         taskPositioner.onDragPositioningEnd(
+            DISPLAY_ID,
             STARTING_BOUNDS.left.toFloat() + 10,
             STARTING_BOUNDS.top.toFloat() + 10
         )
@@ -300,6 +311,7 @@
     fun testDragResize_drag_setBoundsNotRunIfDragEndsInDisallowedEndArea() = runOnUiThread {
         taskPositioner.onDragPositioningStart(
                 CTRL_TYPE_UNDEFINED, // drag
+                DISPLAY_ID,
                 STARTING_BOUNDS.left.toFloat(),
                 STARTING_BOUNDS.top.toFloat()
         )
@@ -307,11 +319,12 @@
         val newX = STARTING_BOUNDS.left.toFloat() + 5
         val newY = DISALLOWED_AREA_FOR_END_BOUNDS_HEIGHT.toFloat() - 1
         taskPositioner.onDragPositioningMove(
+                DISPLAY_ID,
                 newX,
                 newY
         )
 
-        taskPositioner.onDragPositioningEnd(newX, newY)
+        taskPositioner.onDragPositioningEnd(DISPLAY_ID, newX, newY)
 
         verify(mockShellTaskOrganizer, never()).applyTransaction(argThat { wct ->
             return@argThat wct.changes.any { (token, change) ->
@@ -326,6 +339,7 @@
         mockDesktopWindowDecoration.mHasGlobalFocus = false
         taskPositioner.onDragPositioningStart(
                 CTRL_TYPE_RIGHT, // Resize right
+                DISPLAY_ID,
                 STARTING_BOUNDS.left.toFloat(),
                 STARTING_BOUNDS.top.toFloat()
         )
@@ -342,6 +356,7 @@
         mockDesktopWindowDecoration.mHasGlobalFocus = true
         taskPositioner.onDragPositioningStart(
                 CTRL_TYPE_RIGHT, // Resize right
+                DISPLAY_ID,
                 STARTING_BOUNDS.left.toFloat(),
                 STARTING_BOUNDS.top.toFloat()
         )
@@ -358,6 +373,7 @@
         mockDesktopWindowDecoration.mHasGlobalFocus = false
         taskPositioner.onDragPositioningStart(
                 CTRL_TYPE_UNDEFINED, // drag
+                DISPLAY_ID,
                 STARTING_BOUNDS.left.toFloat(),
                 STARTING_BOUNDS.top.toFloat()
         )
@@ -422,11 +438,13 @@
 
         taskPositioner.onDragPositioningStart(
                 CTRL_TYPE_TOP or CTRL_TYPE_RIGHT,
+                DISPLAY_ID,
                 STARTING_BOUNDS.left.toFloat(),
                 STARTING_BOUNDS.top.toFloat()
         )
 
         taskPositioner.onDragPositioningMove(
+                DISPLAY_ID,
                 STARTING_BOUNDS.left.toFloat() - 20,
                 STARTING_BOUNDS.top.toFloat() - 20
         )
@@ -436,6 +454,7 @@
         verify(mockDragEventListener, times(1)).onDragMove(eq(TASK_ID))
 
         taskPositioner.onDragPositioningEnd(
+                DISPLAY_ID,
                 STARTING_BOUNDS.left.toFloat(),
                 STARTING_BOUNDS.top.toFloat()
         )
@@ -501,15 +520,18 @@
     ) {
         taskPositioner.onDragPositioningStart(
             ctrlType,
+            DISPLAY_ID,
             startX,
             startY
         )
         taskPositioner.onDragPositioningMove(
+            DISPLAY_ID,
             endX,
             endY
         )
 
         taskPositioner.onDragPositioningEnd(
+            DISPLAY_ID,
             endX,
             endY
         )
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/common/WindowDecorTaskResourceLoaderTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/common/WindowDecorTaskResourceLoaderTest.kt
new file mode 100644
index 0000000..1ec0fe7
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/common/WindowDecorTaskResourceLoaderTest.kt
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.common
+
+import android.app.ActivityManager
+import android.content.ComponentName
+import android.content.Context
+import android.content.Intent
+import android.content.pm.ActivityInfo
+import android.content.pm.ApplicationInfo
+import android.content.pm.PackageManager
+import android.graphics.drawable.Drawable
+import android.os.UserHandle
+import android.testing.AndroidTestingRunner
+import android.testing.TestableContext
+import androidx.test.filters.SmallTest
+import com.android.launcher3.icons.BaseIconFactory
+import com.android.launcher3.icons.BaseIconFactory.MODE_DEFAULT
+import com.android.launcher3.icons.IconProvider
+import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.TestRunningTaskInfoBuilder
+import com.android.wm.shell.TestShellExecutor
+import com.android.wm.shell.sysui.ShellController
+import com.android.wm.shell.sysui.ShellInit
+import com.android.wm.shell.sysui.UserChangeListener
+import com.android.wm.shell.windowdecor.common.WindowDecorTaskResourceLoader.AppResources
+import com.google.common.truth.Truth.assertThat
+import org.junit.Assert.assertThrows
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyFloat
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.kotlin.any
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.verifyZeroInteractions
+import org.mockito.kotlin.whenever
+
+/**
+ * Tests for [WindowDecorTaskResourceLoader].
+ *
+ * Build/Install/Run: atest WindowDecorTaskResourceLoaderTest
+ */
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class WindowDecorTaskResourceLoaderTest : ShellTestCase() {
+    private val testExecutor = TestShellExecutor()
+    private val shellInit = ShellInit(testExecutor)
+    private val mockShellController = mock<ShellController>()
+    private val mockPackageManager = mock<PackageManager>()
+    private val mockIconProvider = mock<IconProvider>()
+    private val mockHeaderIconFactory = mock<BaseIconFactory>()
+    private val mockVeilIconFactory = mock<BaseIconFactory>()
+
+    private lateinit var spyContext: TestableContext
+    private lateinit var loader: WindowDecorTaskResourceLoader
+
+    private val userChangeListenerCaptor = argumentCaptor<UserChangeListener>()
+    private val userChangeListener: UserChangeListener by lazy {
+        userChangeListenerCaptor.firstValue
+    }
+
+    @Before
+    fun setUp() {
+        spyContext = spy(mContext)
+        spyContext.setMockPackageManager(mockPackageManager)
+        doReturn(spyContext).whenever(spyContext).createContextAsUser(any(), anyInt())
+        loader =
+            WindowDecorTaskResourceLoader(
+                context = spyContext,
+                shellInit = shellInit,
+                shellController = mockShellController,
+                shellCommandHandler = mock(),
+                iconProvider = mockIconProvider,
+                headerIconFactory = mockHeaderIconFactory,
+                veilIconFactory = mockVeilIconFactory,
+            )
+        shellInit.init()
+        testExecutor.flushAll()
+        verify(mockShellController).addUserChangeListener(userChangeListenerCaptor.capture())
+    }
+
+    @Test
+    fun testGetName_notCached_loadsResourceAndCaches() {
+        val task = createTaskInfo(context.userId)
+        loader.onWindowDecorCreated(task)
+
+        loader.getName(task)
+
+        verify(mockPackageManager).getApplicationLabel(task.topActivityInfo!!.applicationInfo)
+        assertThat(loader.taskToResourceCache[task.taskId]?.appName).isNotNull()
+    }
+
+    @Test
+    fun testGetName_cached_returnsFromCache() {
+        val task = createTaskInfo(context.userId)
+        loader.onWindowDecorCreated(task)
+        loader.taskToResourceCache[task.taskId] = AppResources("App Name", mock(), mock())
+
+        loader.getName(task)
+
+        verifyZeroInteractions(
+            mockPackageManager,
+            mockIconProvider,
+            mockHeaderIconFactory,
+            mockVeilIconFactory,
+        )
+    }
+
+    @Test
+    fun testGetHeaderIcon_notCached_loadsResourceAndCaches() {
+        val task = createTaskInfo(context.userId)
+        loader.onWindowDecorCreated(task)
+
+        loader.getHeaderIcon(task)
+
+        verify(mockHeaderIconFactory).createIconBitmap(any(), anyFloat())
+        assertThat(loader.taskToResourceCache[task.taskId]?.appIcon).isNotNull()
+    }
+
+    @Test
+    fun testGetHeaderIcon_cached_returnsFromCache() {
+        val task = createTaskInfo(context.userId)
+        loader.onWindowDecorCreated(task)
+        loader.taskToResourceCache[task.taskId] = AppResources("App Name", mock(), mock())
+
+        loader.getHeaderIcon(task)
+
+        verifyZeroInteractions(mockPackageManager, mockIconProvider, mockHeaderIconFactory)
+    }
+
+    @Test
+    fun testGetVeilIcon_notCached_loadsResourceAndCaches() {
+        val task = createTaskInfo(context.userId)
+        loader.onWindowDecorCreated(task)
+
+        loader.getVeilIcon(task)
+
+        verify(mockVeilIconFactory).createScaledBitmap(any(), anyInt())
+        assertThat(loader.taskToResourceCache[task.taskId]?.veilIcon).isNotNull()
+    }
+
+    @Test
+    fun testGetVeilIcon_cached_returnsFromCache() {
+        val task = createTaskInfo(context.userId)
+        loader.onWindowDecorCreated(task)
+        loader.taskToResourceCache[task.taskId] = AppResources("App Name", mock(), mock())
+
+        loader.getVeilIcon(task)
+
+        verifyZeroInteractions(mockPackageManager, mockIconProvider, mockVeilIconFactory)
+    }
+
+    @Test
+    fun testUserChange_updatesContext() {
+        val newUser = 5000
+        val newContext = mock<Context>()
+
+        userChangeListener.onUserChanged(newUser, newContext)
+
+        assertThat(loader.currentUserContext).isEqualTo(newContext)
+    }
+
+    @Test
+    fun testUserChange_clearsCache() {
+        val newUser = 5000
+        val newContext = mock<Context>()
+        val task = createTaskInfo(context.userId)
+        loader.onWindowDecorCreated(task)
+        loader.getName(task)
+
+        userChangeListener.onUserChanged(newUser, newContext)
+
+        assertThat(loader.taskToResourceCache[task.taskId]?.appName).isNull()
+    }
+
+    @Test
+    fun testGet_nonexistentDecor_throws() {
+        val task = createTaskInfo(context.userId)
+
+        assertThrows(Exception::class.java) { loader.getName(task) }
+    }
+
+    private fun createTaskInfo(userId: Int): ActivityManager.RunningTaskInfo {
+        val appIconDrawable = mock<Drawable>()
+        val badgedAppIconDrawable = mock<Drawable>()
+        val activityInfo = ActivityInfo().apply { applicationInfo = ApplicationInfo() }
+        val componentName = ComponentName("com.foo", "BarActivity")
+        whenever(mockPackageManager.getActivityInfo(eq(componentName), anyInt()))
+            .thenReturn(activityInfo)
+        whenever(mockPackageManager.getApplicationLabel(activityInfo.applicationInfo))
+            .thenReturn("Test App")
+        whenever(mockPackageManager.getUserBadgedIcon(appIconDrawable, UserHandle.of(userId)))
+            .thenReturn(badgedAppIconDrawable)
+        whenever(mockIconProvider.getIcon(activityInfo)).thenReturn(appIconDrawable)
+        whenever(mockHeaderIconFactory.createIconBitmap(badgedAppIconDrawable, 1f))
+            .thenReturn(mock())
+        whenever(mockVeilIconFactory.createScaledBitmap(appIconDrawable, MODE_DEFAULT))
+            .thenReturn(mock())
+        return TestRunningTaskInfoBuilder()
+            .setUserId(userId)
+            .setBaseIntent(Intent().apply { component = componentName })
+            .build()
+            .apply { topActivityInfo = activityInfo }
+    }
+}
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 193c2c2..997ece6e 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
@@ -32,7 +32,10 @@
 import com.android.wm.shell.desktopmode.ToggleResizeDesktopTaskTransitionHandler
 import com.android.wm.shell.transition.Transitions
 import com.android.wm.shell.windowdecor.DesktopModeWindowDecoration
+import com.android.wm.shell.windowdecor.common.WindowDecorTaskResourceLoader
 import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.MainCoroutineDispatcher
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -47,6 +50,8 @@
 @RunWith(AndroidTestingRunner::class)
 class DesktopTilingDecorViewModelTest : ShellTestCase() {
     private val contextMock: Context = mock()
+    private val mainDispatcher: MainCoroutineDispatcher = mock()
+    private val bgScope: CoroutineScope = mock()
     private val displayControllerMock: DisplayController = mock()
     private val rootTdaOrganizerMock: RootTaskDisplayAreaOrganizer = mock()
     private val syncQueueMock: SyncTransactionQueue = mock()
@@ -61,6 +66,7 @@
 
     private val desktopModeWindowDecorationMock: DesktopModeWindowDecoration = mock()
     private val desktopTilingDecoration: DesktopTilingWindowDecoration = mock()
+    private val taskResourceLoader: WindowDecorTaskResourceLoader = mock()
     private lateinit var desktopTilingDecorViewModel: DesktopTilingDecorViewModel
 
     @Before
@@ -68,6 +74,8 @@
         desktopTilingDecorViewModel =
             DesktopTilingDecorViewModel(
                 contextMock,
+                mainDispatcher,
+                bgScope,
                 displayControllerMock,
                 rootTdaOrganizerMock,
                 syncQueueMock,
@@ -77,6 +85,7 @@
                 returnToDragStartAnimatorMock,
                 userRepositories,
                 desktopModeEventLogger,
+                taskResourceLoader,
             )
         whenever(contextMock.createContextAsUser(any(), any())).thenReturn(contextMock)
     }
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 95e2151..2f15c2e 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
@@ -45,7 +45,10 @@
 import com.android.wm.shell.transition.Transitions
 import com.android.wm.shell.windowdecor.DesktopModeWindowDecoration
 import com.android.wm.shell.windowdecor.DragResizeWindowGeometry
+import com.android.wm.shell.windowdecor.common.WindowDecorTaskResourceLoader
 import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.MainCoroutineDispatcher
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -99,6 +102,9 @@
     private val desktopTilingDividerWindowManager: DesktopTilingDividerWindowManager = mock()
     private val motionEvent: MotionEvent = mock()
     private val desktopRepository: DesktopRepository = mock()
+    private val mainDispatcher: MainCoroutineDispatcher = mock()
+    private val bgScope: CoroutineScope = mock()
+    private val taskResourceLoader: WindowDecorTaskResourceLoader = mock()
     private lateinit var tilingDecoration: DesktopTilingWindowDecoration
 
     private val split_divider_width = 10
@@ -110,8 +116,11 @@
         tilingDecoration =
             DesktopTilingWindowDecoration(
                 context,
+                mainDispatcher,
+                bgScope,
                 syncQueue,
                 displayController,
+                taskResourceLoader,
                 displayId,
                 rootTdaOrganizer,
                 transitions,
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index e2db2c9..677fd86 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -154,7 +154,10 @@
                 "libstatssocket_lazy",
                 "libtonemap",
             ],
-            whole_static_libs: ["hwui_flags_cc_lib"],
+            whole_static_libs: [
+                "hwui_flags_cc_lib",
+                "libsurfaceflingerflags",
+            ],
         },
         host: {
             static_libs: [
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index 064cac2..7d01dfb 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -54,6 +54,9 @@
 constexpr bool query_global_priority() {
     return false;
 }
+constexpr bool early_preload_gl_context() {
+    return false;
+}
 }  // namespace hwui_flags
 #endif
 
@@ -291,5 +294,10 @@
     return sResampleGainmapRegions;
 }
 
+bool Properties::earlyPreloadGlContext() {
+    return base::GetBoolProperty(PROPERTY_EARLY_PRELOAD_GL_CONTEXT,
+                                 hwui_flags::early_preload_gl_context());
+}
+
 }  // namespace uirenderer
 }  // namespace android
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index db930f3..280a75a 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -236,6 +236,8 @@
 
 #define PROPERTY_SKIP_EGLMANAGER_TELEMETRY "debug.hwui.skip_eglmanager_telemetry"
 
+#define PROPERTY_EARLY_PRELOAD_GL_CONTEXT "debug.hwui.early_preload_gl_context"
+
 ///////////////////////////////////////////////////////////////////////////////
 // Misc
 ///////////////////////////////////////////////////////////////////////////////
@@ -381,6 +383,7 @@
 
     static bool initializeGlAlways();
     static bool resampleGainmapRegions();
+    static bool earlyPreloadGlContext();
 
 private:
     static StretchEffectBehavior stretchEffectBehavior;
diff --git a/libs/hwui/aconfig/hwui_flags.aconfig b/libs/hwui/aconfig/hwui_flags.aconfig
index e497ea1..76ad2ac 100644
--- a/libs/hwui/aconfig/hwui_flags.aconfig
+++ b/libs/hwui/aconfig/hwui_flags.aconfig
@@ -166,4 +166,11 @@
   metadata {
     purpose: PURPOSE_BUGFIX
   }
+}
+
+flag {
+  name: "early_preload_gl_context"
+  namespace: "core_graphics"
+  description: "Initialize GL context and GraphicBufferAllocater init on renderThread preload. This improves app startup time for apps using GL."
+  bug: "383612849"
 }
\ No newline at end of file
diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp
index cc292d9..b1550b0 100644
--- a/libs/hwui/hwui/Bitmap.cpp
+++ b/libs/hwui/hwui/Bitmap.cpp
@@ -19,6 +19,7 @@
 #include "HardwareBitmapUploader.h"
 #include "Properties.h"
 #ifdef __ANDROID__  // Layoutlib does not support render thread
+#include <com_android_graphics_surfaceflinger_flags.h>
 #include <private/android/AHardwareBufferHelpers.h>
 #include <ui/GraphicBuffer.h>
 #include <ui/GraphicBufferMapper.h>
@@ -562,7 +563,7 @@
 
 bool Bitmap::compress(JavaCompressFormat format, int32_t quality, SkWStream* stream) {
 #ifdef __ANDROID__  // TODO: This isn't built for host for some reason?
-    if (hasGainmap() && format == JavaCompressFormat::Jpeg) {
+    if (hasGainmap()) {
         SkBitmap baseBitmap = getSkBitmap();
         SkBitmap gainmapBitmap = gainmap()->bitmap->getSkBitmap();
         if (gainmapBitmap.colorType() == SkColorType::kAlpha_8_SkColorType) {
@@ -572,12 +573,27 @@
             greyGainmap.setPixelRef(sk_ref_sp(gainmapBitmap.pixelRef()), 0, 0);
             gainmapBitmap = std::move(greyGainmap);
         }
-        SkJpegEncoder::Options options{.fQuality = quality};
-        return SkJpegGainmapEncoder::EncodeHDRGM(stream, baseBitmap.pixmap(), options,
-                                                 gainmapBitmap.pixmap(), options, gainmap()->info);
+        switch (format) {
+            case JavaCompressFormat::Jpeg: {
+                SkJpegEncoder::Options options{.fQuality = quality};
+                return SkJpegGainmapEncoder::EncodeHDRGM(stream, baseBitmap.pixmap(), options,
+                                                         gainmapBitmap.pixmap(), options,
+                                                         gainmap()->info);
+            }
+            case JavaCompressFormat::Png: {
+                if (com::android::graphics::surfaceflinger::flags::true_hdr_screenshots()) {
+                    SkGainmapInfo info = gainmap()->info;
+                    SkPngEncoder::Options options{.fGainmap = &gainmapBitmap.pixmap(),
+                                                  .fGainmapInfo = &info};
+                    return SkPngEncoder::Encode(stream, baseBitmap.pixmap(), options);
+                }
+                // fallthrough if we're not supporting HDR screenshots
+            }
+            default:
+                ALOGI("Format: %d doesn't support gainmap compression!", format);
+        }
     }
 #endif
-
     SkBitmap skbitmap;
     getSkBitmap(&skbitmap);
     return compress(skbitmap, format, quality, stream);
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index 92c6ad1..69fe40c 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -25,6 +25,7 @@
 #include <private/android/choreographer.h>
 #include <sys/resource.h>
 #include <ui/FatVector.h>
+#include <ui/GraphicBufferAllocator.h>
 #include <utils/Condition.h>
 #include <utils/Log.h>
 #include <utils/Mutex.h>
@@ -518,11 +519,18 @@
 void RenderThread::preload() {
     // EGL driver is always preloaded only if HWUI renders with GL.
     if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaGL) {
-        std::thread eglInitThread([]() { eglGetDisplay(EGL_DEFAULT_DISPLAY); });
-        eglInitThread.detach();
+        if (Properties::earlyPreloadGlContext()) {
+            queue().post([this]() { requireGlContext(); });
+        } else {
+            std::thread eglInitThread([]() { eglGetDisplay(EGL_DEFAULT_DISPLAY); });
+            eglInitThread.detach();
+        }
     } else {
         requireVkContext();
     }
+    if (Properties::earlyPreloadGlContext()) {
+        queue().post([]() { GraphicBufferAllocator::getInstance(); });
+    }
     HardwareBitmapUploader::initialize();
 }
 
diff --git a/media/java/android/media/AudioDevicePort.java b/media/java/android/media/AudioDevicePort.java
index 4b3962e..9e9bbee 100644
--- a/media/java/android/media/AudioDevicePort.java
+++ b/media/java/android/media/AudioDevicePort.java
@@ -210,6 +210,27 @@
         return super.equals(o);
     }
 
+    /**
+     * Returns true if the AudioDevicePort passed as argument represents the same device (same
+     * type and same address). This is different from equals() in that the port IDs are not compared
+     * which allows matching devices across native audio server restarts.
+     * @param other the other audio device port to compare to.
+     * @return true if both device port correspond to the same audio device, false otherwise.
+     * @hide
+     */
+    public boolean isSameAs(AudioDevicePort other) {
+        if (mType != other.type()) {
+            return false;
+        }
+        if (mAddress == null && other.address() != null) {
+            return false;
+        }
+        if (!mAddress.equals(other.address())) {
+            return false;
+        }
+        return true;
+    }
+
     @Override
     public String toString() {
         String type = (mRole == ROLE_SOURCE ?
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 9beeef4..52eae43 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -8068,15 +8068,15 @@
             ArrayList<AudioDevicePort> ports_A, ArrayList<AudioDevicePort> ports_B, int flags) {
 
         ArrayList<AudioDevicePort> delta_ports = new ArrayList<AudioDevicePort>();
-
-        AudioDevicePort cur_port = null;
         for (int cur_index = 0; cur_index < ports_B.size(); cur_index++) {
             boolean cur_port_found = false;
-            cur_port = ports_B.get(cur_index);
+            AudioDevicePort cur_port = ports_B.get(cur_index);
             for (int prev_index = 0;
                  prev_index < ports_A.size() && !cur_port_found;
                  prev_index++) {
-                cur_port_found = (cur_port.id() == ports_A.get(prev_index).id());
+                // Do not compare devices by port ID as these change when the native
+                // audio server restarts
+                cur_port_found = cur_port.isSameAs(ports_A.get(prev_index));
             }
 
             if (!cur_port_found) {
@@ -8422,13 +8422,10 @@
          * Callback method called when the mediaserver dies
          */
         public void onServiceDied() {
-            synchronized (mDeviceCallbacks) {
-                broadcastDeviceListChange_sync(null);
-            }
+           // Nothing to do here
         }
     }
 
-
     /**
      * @hide
      * Abstract class to receive event notification about audioserver process state.
@@ -8469,9 +8466,13 @@
     /**
      * @hide
      * Registers a callback for notification of audio server state changes.
-     * @param executor {@link Executor} to handle the callbacks
-     * @param stateCallback the callback to receive the audio server state changes
-     *        To remove the callabck, pass a null reference for both executor and stateCallback.
+     * @param executor {@link Executor} to handle the callbacks. Must be non null.
+     * @param stateCallback the callback to receive the audio server state changes.
+     *                      Must be non null. To remove the callabck,
+     *                      call {@link #clearAudioServerStateCallback()}
+     * @throws IllegalArgumentException If a null argument is specified.
+     * @throws IllegalStateException If a callback is already registered
+     * *
      */
     @SystemApi
     public void setAudioServerStateCallback(@NonNull Executor executor,
diff --git a/media/java/android/media/AudioPortEventHandler.java b/media/java/android/media/AudioPortEventHandler.java
index 763eb29..5685710 100644
--- a/media/java/android/media/AudioPortEventHandler.java
+++ b/media/java/android/media/AudioPortEventHandler.java
@@ -97,17 +97,15 @@
 
                         ArrayList<AudioPort> ports = new ArrayList<AudioPort>();
                         ArrayList<AudioPatch> patches = new ArrayList<AudioPatch>();
-                        if (msg.what != AUDIOPORT_EVENT_SERVICE_DIED) {
-                            int status = AudioManager.updateAudioPortCache(ports, patches, null);
-                            if (status != AudioManager.SUCCESS) {
-                                // Since audio ports and audio patches are not null, the return
-                                // value could be ERROR due to inconsistency between port generation
-                                // and patch generation. In this case, we need to reschedule the
-                                // message to make sure the native callback is done.
-                                sendMessageDelayed(obtainMessage(msg.what, msg.obj),
-                                        RESCHEDULE_MESSAGE_DELAY_MS);
-                                return;
-                            }
+                        int status = AudioManager.updateAudioPortCache(ports, patches, null);
+                        if (status != AudioManager.SUCCESS) {
+                            // Since audio ports and audio patches are not null, the return
+                            // value could be ERROR due to inconsistency between port generation
+                            // and patch generation. In this case, we need to reschedule the
+                            // message to make sure the native callback is done.
+                            sendMessageDelayed(obtainMessage(msg.what, msg.obj),
+                                    RESCHEDULE_MESSAGE_DELAY_MS);
+                            return;
                         }
 
                         switch (msg.what) {
diff --git a/media/java/android/media/Image.java b/media/java/android/media/Image.java
index d6fe6825..486063e 100644
--- a/media/java/android/media/Image.java
+++ b/media/java/android/media/Image.java
@@ -180,6 +180,15 @@
      *     a semi-planar format, the Cb plane can also be treated as an interleaved Cb/Cr plane.
      *   </td>
      * </tr>
+     * <tr>
+     *   <td>{@link android.graphics.ImageFormat#YCBCR_P210 YCBCR_P210}</td>
+     *   <td>3</td>
+     *   <td>P210 is a 4:2:2 YCbCr semiplanar format comprised of a WxH Y plane
+     *     followed by a WxH Cb and Cr planes. Each sample is represented by a 16-bit
+     *     little-endian value, with the lower 6 bits set to zero. Since this is guaranteed to be
+     *     a semi-planar format, the Cb plane can also be treated as an interleaved Cb/Cr plane.
+     *   </td>
+     * </tr>
      * </table>
      *
      * @see android.graphics.ImageFormat
diff --git a/media/java/android/media/ImageUtils.java b/media/java/android/media/ImageUtils.java
index c767806..7c6ba83 100644
--- a/media/java/android/media/ImageUtils.java
+++ b/media/java/android/media/ImageUtils.java
@@ -56,6 +56,7 @@
             case ImageFormat.YUV_420_888:
             case ImageFormat.NV21:
             case ImageFormat.YCBCR_P010:
+            case ImageFormat.YCBCR_P210:
                 return 3;
             case ImageFormat.NV16:
                 return 2;
@@ -95,6 +96,7 @@
         switch(hardwareBufferFormat) {
             case HardwareBuffer.YCBCR_420_888:
             case HardwareBuffer.YCBCR_P010:
+            case HardwareBuffer.YCBCR_P210:
                 return 3;
             case HardwareBuffer.RGBA_8888:
             case HardwareBuffer.RGBX_8888:
@@ -281,6 +283,7 @@
             case PixelFormat.RGBA_8888:
             case PixelFormat.RGBX_8888:
             case PixelFormat.RGBA_1010102:
+            case ImageFormat.YCBCR_P210:
                 estimatedBytePerPixel = 4.0;
                 break;
             default:
@@ -335,6 +338,12 @@
                 return new Size(image.getWidth(), image.getHeight());
             case ImageFormat.PRIVATE:
                 return new Size(0, 0);
+            case ImageFormat.YCBCR_P210:
+                if (planeIdx == 0) {
+                    return new Size(image.getWidth(), image.getHeight());
+                } else {
+                    return new Size(image.getWidth() / 2, image.getHeight());
+                }
             default:
                 if (Log.isLoggable(IMAGEUTILS_LOG_TAG, Log.VERBOSE)) {
                     Log.v(IMAGEUTILS_LOG_TAG, "getEffectivePlaneSizeForImage() uses"
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index 82e9503..36f62da 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -6158,11 +6158,14 @@
                     buffer.limit(buffer.position() + Utils.divUp(bitDepth, 8)
                             + (mHeight / vert - 1) * rowInc + (mWidth / horiz - 1) * colInc);
                     mPlanes[ix] = new MediaPlane(buffer.slice(), rowInc, colInc);
-                    if ((mFormat == ImageFormat.YUV_420_888 || mFormat == ImageFormat.YCBCR_P010)
+                    if ((mFormat == ImageFormat.YUV_420_888 || mFormat == ImageFormat.YCBCR_P010
+                                || mFormat == ImageFormat.YCBCR_P210)
                             && ix == 1) {
                         cbPlaneOffset = planeOffset;
                     } else if ((mFormat == ImageFormat.YUV_420_888
-                            || mFormat == ImageFormat.YCBCR_P010) && ix == 2) {
+                                       || mFormat == ImageFormat.YCBCR_P010
+                                       || mFormat == ImageFormat.YCBCR_P210)
+                            && ix == 2) {
                         crPlaneOffset = planeOffset;
                     }
                 }
@@ -6172,7 +6175,7 @@
             }
 
             // Validate chroma semiplanerness.
-            if (mFormat == ImageFormat.YCBCR_P010) {
+            if (mFormat == ImageFormat.YCBCR_P010 || mFormat == ImageFormat.YCBCR_P210) {
                 if (crPlaneOffset != cbPlaneOffset + planeOffsetInc) {
                     throw new UnsupportedOperationException("Invalid plane offsets"
                     + " cbPlaneOffset: " + cbPlaneOffset + " crPlaneOffset: " + crPlaneOffset);
diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java
index 5038754..bcb7001 100644
--- a/media/java/android/media/MediaFormat.java
+++ b/media/java/android/media/MediaFormat.java
@@ -53,7 +53,7 @@
  * The format of the media data is specified as key/value pairs. Keys are strings. Values can
  * be integer, long, float, String or ByteBuffer.
  * <p>
- * The feature metadata is specificed as string/boolean pairs.
+ * The feature metadata is specified as string/boolean pairs.
  * <p>
  * Keys common to all audio/video formats, <b>all keys not marked optional are mandatory</b>:
  *
@@ -1244,12 +1244,12 @@
 
     /**
     * An optional key describing the desired encoder latency in frames. This is an optional
-    * parameter that applies only to video encoders. If encoder supports it, it should ouput
+    * parameter that applies only to video encoders. If encoder supports it, it should output
     * at least one output frame after being queued the specified number of frames. This key
     * is ignored if the video encoder does not support the latency feature. Use the output
     * format to verify that this feature was enabled and the actual value used by the encoder.
     * <p>
-    * If the key is not specified, the default latency will be implenmentation specific.
+    * If the key is not specified, the default latency will be implementation specific.
     * The associated value is an integer.
     */
     public static final String KEY_LATENCY = "latency";
@@ -1507,16 +1507,16 @@
      */
     public static final String KEY_COLOR_STANDARD = "color-standard";
 
-    /** BT.709 color chromacity coordinates with KR = 0.2126, KB = 0.0722. */
+    /** BT.709 color chromaticity coordinates with KR = 0.2126, KB = 0.0722. */
     public static final int COLOR_STANDARD_BT709 = 1;
 
-    /** BT.601 625 color chromacity coordinates with KR = 0.299, KB = 0.114. */
+    /** BT.601 625 color chromaticity coordinates with KR = 0.299, KB = 0.114. */
     public static final int COLOR_STANDARD_BT601_PAL = 2;
 
-    /** BT.601 525 color chromacity coordinates with KR = 0.299, KB = 0.114. */
+    /** BT.601 525 color chromaticity coordinates with KR = 0.299, KB = 0.114. */
     public static final int COLOR_STANDARD_BT601_NTSC = 4;
 
-    /** BT.2020 color chromacity coordinates with KR = 0.2627, KB = 0.0593. */
+    /** BT.2020 color chromaticity coordinates with KR = 0.2627, KB = 0.0593. */
     public static final int COLOR_STANDARD_BT2020 = 6;
 
     /** @hide */
@@ -2150,7 +2150,7 @@
      * Sets the value of a string key.
      * <p>
      * If value is {@code null}, it sets a null value that behaves similarly to a missing key.
-     * This could be used prior to API level {@link android os.Build.VERSION_CODES#Q} to effectively
+     * This could be used prior to API level {@link android.os.Build.VERSION_CODES#Q} to effectively
      * remove a key.
      */
     public final void setString(@NonNull String name, @Nullable String value) {
@@ -2161,7 +2161,7 @@
      * Sets the value of a ByteBuffer key.
      * <p>
      * If value is {@code null}, it sets a null value that behaves similarly to a missing key.
-     * This could be used prior to API level {@link android os.Build.VERSION_CODES#Q} to effectively
+     * This could be used prior to API level {@link android.os.Build.VERSION_CODES#Q} to effectively
      * remove a key.
      */
     public final void setByteBuffer(@NonNull String name, @Nullable ByteBuffer bytes) {
diff --git a/media/java/android/media/MediaRoute2Info.java b/media/java/android/media/MediaRoute2Info.java
index 037b97a..bbb03e7 100644
--- a/media/java/android/media/MediaRoute2Info.java
+++ b/media/java/android/media/MediaRoute2Info.java
@@ -21,6 +21,7 @@
 
 import static com.android.media.flags.Flags.FLAG_ENABLE_AUDIO_POLICIES_DEVICE_AND_BLUETOOTH_CONTROLLER;
 import static com.android.media.flags.Flags.FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES;
+import static com.android.media.flags.Flags.FLAG_ENABLE_MEDIA_ROUTE_2_INFO_PROVIDER_PACKAGE_NAME;
 import static com.android.media.flags.Flags.FLAG_ENABLE_MIRRORING_IN_MEDIA_ROUTER_2;
 import static com.android.media.flags.Flags.FLAG_ENABLE_NEW_MEDIA_ROUTE_2_INFO_TYPES;
 import static com.android.media.flags.Flags.FLAG_ENABLE_NEW_WIRED_MEDIA_ROUTE_2_INFO_TYPES;
@@ -631,7 +632,7 @@
     @ConnectionState
     private final int mConnectionState;
     private final String mClientPackageName;
-    private final String mPackageName;
+    private final String mProviderPackageName;
     @PlaybackVolume private final int mVolumeHandling;
     private final int mVolumeMax;
     private final int mVolume;
@@ -655,7 +656,7 @@
         mDescription = builder.mDescription;
         mConnectionState = builder.mConnectionState;
         mClientPackageName = builder.mClientPackageName;
-        mPackageName = builder.mPackageName;
+        mProviderPackageName = builder.mProviderPackageName;
         mVolumeHandling = builder.mVolumeHandling;
         mVolumeMax = builder.mVolumeMax;
         mVolume = builder.mVolume;
@@ -681,7 +682,7 @@
         mDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
         mConnectionState = in.readInt();
         mClientPackageName = in.readString();
-        mPackageName = in.readString();
+        mProviderPackageName = in.readString();
         mVolumeHandling = in.readInt();
         mVolumeMax = in.readInt();
         mVolume = in.readInt();
@@ -801,14 +802,19 @@
     }
 
     /**
-     * Gets the package name of the provider that published the route.
-     * <p>
-     * It is set by the system service.
-     * @hide
+     * Gets the package name of the {@link MediaRoute2ProviderService provider} that published the
+     * route, or null if it has not yet been populated.
+     *
+     * <p>The package name of the route provider is populated by the system as part of {@link
+     * MediaRoute2ProviderService#notifyRoutes(java.util.Collection)}. As a result, it's expectable
+     * that a {@link MediaRoute2Info} instance that hasn't yet been published will have a null
+     * provider package name. Otherwise, routes obtained via {@link MediaRouter2} should have a
+     * populated provider package name.
      */
+    @FlaggedApi(FLAG_ENABLE_MEDIA_ROUTE_2_INFO_PROVIDER_PACKAGE_NAME)
     @Nullable
-    public String getPackageName() {
-        return mPackageName;
+    public String getProviderPackageName() {
+        return mProviderPackageName;
     }
 
     /**
@@ -928,6 +934,15 @@
     }
 
     /**
+     * Returns whether this route supports {@link #FLAG_ROUTING_TYPE_REMOTE remote routing}.
+     *
+     * @hide
+     */
+    public boolean supportsRemoteRouting() {
+        return (mRoutingTypeFlags & MediaRoute2Info.FLAG_ROUTING_TYPE_REMOTE) != 0;
+    }
+
+    /**
      * Returns true if the route info has all of the required field.
      * A route is valid if and only if it is obtained from
      * {@link com.android.server.media.MediaRouterService}.
@@ -943,10 +958,13 @@
 
     /**
      * Returns whether this route is visible to the package with the given name.
+     *
      * @hide
      */
+    @FlaggedApi(FLAG_ENABLE_MEDIA_ROUTE_2_INFO_PROVIDER_PACKAGE_NAME)
     public boolean isVisibleTo(String packageName) {
-        return !mIsVisibilityRestricted || getPackageName().equals(packageName)
+        return !mIsVisibilityRestricted
+                || TextUtils.equals(getProviderPackageName(), packageName)
                 || mAllowedPackages.contains(packageName);
     }
 
@@ -1020,7 +1038,7 @@
         pw.println(indent + "mDescription=" + mDescription);
         pw.println(indent + "mConnectionState=" + mConnectionState);
         pw.println(indent + "mClientPackageName=" + mClientPackageName);
-        pw.println(indent + "mPackageName=" + mPackageName);
+        pw.println(indent + "mProviderPackageName=" + mProviderPackageName);
 
         dumpVolume(pw, indent);
 
@@ -1059,7 +1077,7 @@
                 && Objects.equals(mDescription, other.mDescription)
                 && (mConnectionState == other.mConnectionState)
                 && Objects.equals(mClientPackageName, other.mClientPackageName)
-                && Objects.equals(mPackageName, other.mPackageName)
+                && Objects.equals(mProviderPackageName, other.mProviderPackageName)
                 && (mVolumeHandling == other.mVolumeHandling)
                 && (mVolumeMax == other.mVolumeMax)
                 && (mVolume == other.mVolume)
@@ -1086,7 +1104,7 @@
                 mDescription,
                 mConnectionState,
                 mClientPackageName,
-                mPackageName,
+                mProviderPackageName,
                 mVolumeHandling,
                 mVolumeMax,
                 mVolume,
@@ -1162,7 +1180,7 @@
         TextUtils.writeToParcel(mDescription, dest, flags);
         dest.writeInt(mConnectionState);
         dest.writeString(mClientPackageName);
-        dest.writeString(mPackageName);
+        dest.writeString(mProviderPackageName);
         dest.writeInt(mVolumeHandling);
         dest.writeInt(mVolumeMax);
         dest.writeInt(mVolume);
@@ -1314,7 +1332,7 @@
         @ConnectionState
         private int mConnectionState;
         private String mClientPackageName;
-        private String mPackageName;
+        private String mProviderPackageName;
         @PlaybackVolume private int mVolumeHandling = PLAYBACK_VOLUME_FIXED;
         private int mVolumeMax;
         private int mVolume;
@@ -1387,7 +1405,7 @@
             mDescription = routeInfo.mDescription;
             mConnectionState = routeInfo.mConnectionState;
             mClientPackageName = routeInfo.mClientPackageName;
-            mPackageName = routeInfo.mPackageName;
+            mProviderPackageName = routeInfo.mProviderPackageName;
             mVolumeHandling = routeInfo.mVolumeHandling;
             mVolumeMax = routeInfo.mVolumeMax;
             mVolume = routeInfo.mVolume;
@@ -1534,11 +1552,13 @@
 
         /**
          * Sets the package name of the route.
+         *
          * @hide
          */
+        // It is set by the MediaRouterService.
         @NonNull
-        public Builder setPackageName(@NonNull String packageName) {
-            mPackageName = packageName;
+        public Builder setProviderPackageName(@NonNull String providerPackageName) {
+            mProviderPackageName = providerPackageName;
             return this;
         }
 
diff --git a/media/java/android/media/MediaRoute2ProviderInfo.java b/media/java/android/media/MediaRoute2ProviderInfo.java
index 809ee23..bcc8cbb 100644
--- a/media/java/android/media/MediaRoute2ProviderInfo.java
+++ b/media/java/android/media/MediaRoute2ProviderInfo.java
@@ -152,11 +152,11 @@
 
         /**
          * Sets the package name and unique id of the provider info.
-         * <p>
-         * The unique id is automatically set by
-         * {@link com.android.server.media.MediaRouterService} and used to identify providers.
-         * The id set by {@link MediaRoute2ProviderService} will be ignored.
-         * </p>
+         *
+         * <p>The unique id is automatically set by {@link
+         * com.android.server.media.MediaRouterService} and used to identify providers. The id set
+         * by {@link MediaRoute2ProviderService} will be ignored.
+         *
          * @hide
          */
         @NonNull
@@ -168,10 +168,11 @@
 
             final ArrayMap<String, MediaRoute2Info> newRoutes = new ArrayMap<>();
             for (Map.Entry<String, MediaRoute2Info> entry : mRoutes.entrySet()) {
-                MediaRoute2Info routeWithProviderId = new MediaRoute2Info.Builder(entry.getValue())
-                        .setPackageName(packageName)
-                        .setProviderId(mUniqueId)
-                        .build();
+                MediaRoute2Info routeWithProviderId =
+                        new MediaRoute2Info.Builder(entry.getValue())
+                                .setProviderPackageName(packageName)
+                                .setProviderId(mUniqueId)
+                                .build();
                 newRoutes.put(routeWithProviderId.getOriginalId(), routeWithProviderId);
             }
 
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index 20108e7..245360c 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -19,6 +19,7 @@
 import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
 import static com.android.media.flags.Flags.FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES;
 import static com.android.media.flags.Flags.FLAG_ENABLE_GET_TRANSFERABLE_ROUTES;
+import static com.android.media.flags.Flags.FLAG_ENABLE_MEDIA_ROUTE_2_INFO_PROVIDER_PACKAGE_NAME;
 import static com.android.media.flags.Flags.FLAG_ENABLE_PRIVILEGED_ROUTING_FOR_MEDIA_ROUTING_CONTROL;
 import static com.android.media.flags.Flags.FLAG_ENABLE_RLP_CALLBACKS_IN_MEDIA_ROUTER2;
 import static com.android.media.flags.Flags.FLAG_ENABLE_SCREEN_OFF_SCANNING;
@@ -809,7 +810,7 @@
      * updates} in order to keep the system UI in a consistent state. You can also call this method
      * at any other point to update the listing preference dynamically.
      *
-     * <p>Any calls to this method from a privileged router will throw an {@link
+     * <p>Calling this method on a proxy router instance will throw an {@link
      * UnsupportedOperationException}.
      *
      * <p>Notes:
@@ -1398,6 +1399,7 @@
         requestCreateController(controller, route, managerRequestId);
     }
 
+    @FlaggedApi(FLAG_ENABLE_MEDIA_ROUTE_2_INFO_PROVIDER_PACKAGE_NAME)
     private List<MediaRoute2Info> getSortedRoutes(
             List<MediaRoute2Info> routes, List<String> packageOrder) {
         if (packageOrder.isEmpty()) {
@@ -1412,11 +1414,13 @@
         ArrayList<MediaRoute2Info> sortedRoutes = new ArrayList<>(routes);
         // take the negative for descending order
         sortedRoutes.sort(
-                Comparator.comparingInt(r -> -packagePriority.getOrDefault(r.getPackageName(), 0)));
+                Comparator.comparingInt(
+                        r -> -packagePriority.getOrDefault(r.getProviderPackageName(), 0)));
         return sortedRoutes;
     }
 
     @GuardedBy("mLock")
+    @FlaggedApi(FLAG_ENABLE_MEDIA_ROUTE_2_INFO_PROVIDER_PACKAGE_NAME)
     private List<MediaRoute2Info> filterRoutesWithCompositePreferenceLocked(
             List<MediaRoute2Info> routes) {
 
@@ -1429,10 +1433,10 @@
                 continue;
             }
             if (!mDiscoveryPreference.getAllowedPackages().isEmpty()
-                    && (route.getPackageName() == null
+                    && (route.getProviderPackageName() == null
                             || !mDiscoveryPreference
                                     .getAllowedPackages()
-                                    .contains(route.getPackageName()))) {
+                                    .contains(route.getProviderPackageName()))) {
                 continue;
             }
             if (mDiscoveryPreference.shouldRemoveDuplicates()) {
@@ -2675,7 +2679,7 @@
         @Override
         public void setRouteListingPreference(@Nullable RouteListingPreference preference) {
             throw new UnsupportedOperationException(
-                    "RouteListingPreference cannot be set by a privileged MediaRouter2 instance.");
+                    "RouteListingPreference cannot be set by a proxy MediaRouter2 instance.");
         }
 
         @Override
@@ -3643,6 +3647,7 @@
             }
         }
 
+        @FlaggedApi(FLAG_ENABLE_MEDIA_ROUTE_2_INFO_PROVIDER_PACKAGE_NAME)
         @Override
         public List<MediaRoute2Info> filterRoutesWithIndividualPreference(
                 List<MediaRoute2Info> routes, RouteDiscoveryPreference discoveryPreference) {
@@ -3652,10 +3657,10 @@
                     continue;
                 }
                 if (!discoveryPreference.getAllowedPackages().isEmpty()
-                        && (route.getPackageName() == null
+                        && (route.getProviderPackageName() == null
                                 || !discoveryPreference
                                         .getAllowedPackages()
-                                        .contains(route.getPackageName()))) {
+                                        .contains(route.getProviderPackageName()))) {
                     continue;
                 }
                 filteredRoutes.add(route);
diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java
index 7e1dccf..3854747 100644
--- a/media/java/android/media/MediaRouter2Manager.java
+++ b/media/java/android/media/MediaRouter2Manager.java
@@ -20,9 +20,11 @@
 import static android.media.MediaRouter2.SCANNING_STATE_WHILE_INTERACTIVE;
 
 import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+import static com.android.media.flags.Flags.FLAG_ENABLE_MEDIA_ROUTE_2_INFO_PROVIDER_PACKAGE_NAME;
 
 import android.Manifest;
 import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
@@ -285,6 +287,7 @@
                 (route) -> sessionInfo.isSystemSession() ^ route.isSystemRoute());
     }
 
+    @FlaggedApi(FLAG_ENABLE_MEDIA_ROUTE_2_INFO_PROVIDER_PACKAGE_NAME)
     private List<MediaRoute2Info> getSortedRoutes(RouteDiscoveryPreference preference) {
         if (!preference.shouldRemoveDuplicates()) {
             synchronized (mRoutesLock) {
@@ -302,12 +305,15 @@
             routes = new ArrayList<>(mRoutes.values());
         }
         // take the negative for descending order
-        routes.sort(Comparator.comparingInt(
-                r -> -packagePriority.getOrDefault(r.getPackageName(), 0)));
+        routes.sort(
+                Comparator.comparingInt(
+                        r -> -packagePriority.getOrDefault(r.getProviderPackageName(), 0)));
         return routes;
     }
 
-    private List<MediaRoute2Info> getFilteredRoutes(@NonNull RoutingSessionInfo sessionInfo,
+    @FlaggedApi(FLAG_ENABLE_MEDIA_ROUTE_2_INFO_PROVIDER_PACKAGE_NAME)
+    private List<MediaRoute2Info> getFilteredRoutes(
+            @NonNull RoutingSessionInfo sessionInfo,
             boolean includeSelectedRoutes,
             @Nullable Predicate<MediaRoute2Info> additionalFilter) {
         Objects.requireNonNull(sessionInfo, "sessionInfo must not be null");
@@ -336,9 +342,10 @@
                 continue;
             }
             if (!discoveryPreference.getAllowedPackages().isEmpty()
-                    && (route.getPackageName() == null
-                    || !discoveryPreference.getAllowedPackages()
-                    .contains(route.getPackageName()))) {
+                    && (route.getProviderPackageName() == null
+                            || !discoveryPreference
+                                    .getAllowedPackages()
+                                    .contains(route.getProviderPackageName()))) {
                 continue;
             }
             if (additionalFilter != null && !additionalFilter.test(route)) {
diff --git a/media/java/android/media/flags/media_better_together.aconfig b/media/java/android/media/flags/media_better_together.aconfig
index bbe8e4e..4398b261 100644
--- a/media/java/android/media/flags/media_better_together.aconfig
+++ b/media/java/android/media/flags/media_better_together.aconfig
@@ -60,6 +60,13 @@
 }
 
 flag {
+    name: "enable_media_route_2_info_provider_package_name"
+    namespace: "media_better_together"
+    description: "Enables a new API to obtain the provider package name from MediaRoute2Info."
+    bug: "378788958"
+}
+
+flag {
     name: "enable_mirroring_in_media_router_2"
     namespace: "media_better_together"
     description: "Enables support for mirroring routes in the MediaRouter2 framework, allowing Output Switcher to offer mirroring routes."
diff --git a/media/java/android/media/projection/MediaProjection.java b/media/java/android/media/projection/MediaProjection.java
index 4f7132a..f7f10df 100644
--- a/media/java/android/media/projection/MediaProjection.java
+++ b/media/java/android/media/projection/MediaProjection.java
@@ -324,19 +324,6 @@
     }
 
     /**
-     * Stops projection.
-     * @hide
-     */
-    public void stop(@StopReason int stopReason) {
-        try {
-            Log.d(TAG, "Content Recording: stopping projection");
-            mImpl.stop(stopReason);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Unable to stop projection", e);
-        }
-    }
-
-    /**
      * Get the underlying IMediaProjection.
      * @hide
      */
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index fc184fe..8419ce7 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -1149,9 +1149,9 @@
 
 static jobject getJavaResources(
         JNIEnv *env,
-        const std::vector<MediaCodec::InstanceResourceInfo>& resources) {
+        const std::vector<InstanceResourceInfo>& resources) {
     jobject resourcesObj = env->NewObject(gArrayListInfo.clazz, gArrayListInfo.ctorId);
-    for (const MediaCodec::InstanceResourceInfo& res : resources) {
+    for (const InstanceResourceInfo& res : resources) {
         ScopedLocalRef<jobject> object{env, env->NewObject(
                 gInstanceResourceInfo.clazz, gInstanceResourceInfo.ctorId)};
         ScopedLocalRef<jstring> nameStr{env, env->NewStringUTF(res.mName.c_str())};
@@ -1169,7 +1169,7 @@
 }
 
 status_t JMediaCodec::getRequiredResources(JNIEnv *env, jobject *resourcesObj) {
-    std::vector<MediaCodec::InstanceResourceInfo> resources;
+    std::vector<InstanceResourceInfo> resources;
     status_t status = mCodec->getRequiredResources(resources);
     if (status != OK) {
         return status;
@@ -3615,9 +3615,9 @@
 
 static jobject getJavaResources(
         JNIEnv *env,
-        const std::vector<MediaCodec::GlobalResourceInfo>& resources) {
+        const std::vector<GlobalResourceInfo>& resources) {
     jobject resourcesObj = env->NewObject(gArrayListInfo.clazz, gArrayListInfo.ctorId);
-    for (const MediaCodec::GlobalResourceInfo& res : resources) {
+    for (const GlobalResourceInfo& res : resources) {
         ScopedLocalRef<jobject> object{env, env->NewObject(
                 gGlobalResourceInfo.clazz, gGlobalResourceInfo.ctorId)};
         ScopedLocalRef<jstring> nameStr{env, env->NewStringUTF(res.mName.c_str())};
@@ -3633,7 +3633,7 @@
 static jobject android_media_MediaCodec_getGloballyAvailableResources(
         JNIEnv *env, jobject thiz) {
     (void)thiz;
-    std::vector<MediaCodec::GlobalResourceInfo> resources;
+    std::vector<GlobalResourceInfo> resources;
     status_t status = MediaCodec::getGloballyAvailableResources(resources);
     if (status != OK) {
         if (status == ERROR_UNSUPPORTED) {
diff --git a/media/jni/android_media_Utils.cpp b/media/jni/android_media_Utils.cpp
index fbebbdc..e8f8644 100644
--- a/media/jni/android_media_Utils.cpp
+++ b/media/jni/android_media_Utils.cpp
@@ -17,13 +17,14 @@
 // #define LOG_NDEBUG 0
 #define LOG_TAG "AndroidMediaUtils"
 
+#include "android_media_Utils.h"
+
+#include <aidl/android/hardware/graphics/common/PixelFormat.h>
 #include <aidl/android/hardware/graphics/common/PlaneLayoutComponentType.h>
 #include <ui/GraphicBufferMapper.h>
 #include <ui/GraphicTypes.h>
 #include <utils/Log.h>
 
-#include "android_media_Utils.h"
-
 #define ALIGN(x, mask) ( ((x) + (mask) - 1) & ~((mask) - 1) )
 
 // Must be in sync with the value in HeicCompositeStream.cpp
@@ -33,6 +34,8 @@
 
 // -----------Utility functions used by ImageReader/Writer JNI-----------------
 
+using AidlPixelFormat = aidl::android::hardware::graphics::common::PixelFormat;
+
 enum {
     IMAGE_MAX_NUM_PLANES = 3,
 };
@@ -74,6 +77,7 @@
         case HAL_PIXEL_FORMAT_BLOB:
         case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED:
         case HAL_PIXEL_FORMAT_YCBCR_P010:
+        case static_cast<int>(AidlPixelFormat::YCBCR_P210):
             return false;
 
         case HAL_PIXEL_FORMAT_YV12:
@@ -105,6 +109,7 @@
             return false;
 
         case HAL_PIXEL_FORMAT_YCBCR_P010:
+        case static_cast<int>(AidlPixelFormat::YCBCR_P210):
         default:
             return true;
     }
@@ -340,6 +345,47 @@
             cb = buffer->data + ySize;
             cr = cb + 2;
 
+            pData = (idx == 0) ? buffer->data : (idx == 1) ? cb : cr;
+            dataSize = (idx == 0) ? ySize : cSize;
+            rStride = buffer->stride * 2;
+            break;
+        case static_cast<int>(AidlPixelFormat::YCBCR_P210):
+            if (buffer->height % 2 != 0) {
+                ALOGE("YCBCR_P210: height (%d) should be a multiple of 2", buffer->height);
+                return BAD_VALUE;
+            }
+
+            if (buffer->width <= 0) {
+                ALOGE("YCBCR_P210: width (%d) should be a > 0", buffer->width);
+                return BAD_VALUE;
+            }
+
+            if (buffer->height <= 0) {
+                ALOGE("YCBCR_210: height (%d) should be a > 0", buffer->height);
+                return BAD_VALUE;
+            }
+            if (buffer->dataCb && buffer->dataCr) {
+                pData = (idx == 0) ? buffer->data : (idx == 1) ? buffer->dataCb : buffer->dataCr;
+                // only map until last pixel
+                if (idx == 0) {
+                    pStride = 2;
+                    rStride = buffer->stride;
+                    dataSize = buffer->stride * (buffer->height - 1) + buffer->width * 2;
+                } else {
+                    pStride = buffer->chromaStep;
+                    rStride = buffer->chromaStride;
+                    dataSize = buffer->chromaStride * (buffer->height - 1) +
+                            buffer->chromaStep * (buffer->width / 2);
+                }
+                break;
+            }
+
+            ySize = (buffer->stride * 2) * buffer->height;
+            cSize = ySize;
+            pStride = (idx == 0) ? 2 : 4;
+            cb = buffer->data + ySize;
+            cr = cb + 2;
+
             pData = (idx == 0) ?  buffer->data : (idx == 1) ?  cb : cr;
             dataSize = (idx == 0) ? ySize : cSize;
             rStride = buffer->stride * 2;
@@ -544,6 +590,80 @@
     return OK;
 }
 
+static status_t extractP210Gralloc4PlaneLayout(sp<GraphicBuffer> buffer, void *pData, int format,
+                                               LockedImage *outputImage) {
+    using aidl::android::hardware::graphics::common::PlaneLayoutComponent;
+    using aidl::android::hardware::graphics::common::PlaneLayoutComponentType;
+
+    GraphicBufferMapper &mapper = GraphicBufferMapper::get();
+    std::vector<ui::PlaneLayout> planeLayouts;
+    status_t res = mapper.getPlaneLayouts(buffer->handle, &planeLayouts);
+    if (res != OK) {
+        return res;
+    }
+    constexpr int64_t Y_PLANE_COMPONENTS = int64_t(PlaneLayoutComponentType::Y);
+    constexpr int64_t CBCR_PLANE_COMPONENTS =
+            int64_t(PlaneLayoutComponentType::CB) | int64_t(PlaneLayoutComponentType::CR);
+    uint8_t *dataY = nullptr;
+    uint8_t *dataCb = nullptr;
+    uint8_t *dataCr = nullptr;
+    uint32_t strideY = 0;
+    uint32_t strideCbCr = 0;
+    for (const ui::PlaneLayout &layout : planeLayouts) {
+        ALOGV("gralloc4 plane: %s", layout.toString().c_str());
+        int64_t components = 0;
+        for (const PlaneLayoutComponent &component : layout.components) {
+            if (component.sizeInBits != 10) {
+                return BAD_VALUE;
+            }
+            components |= component.type.value;
+        }
+        if (components == Y_PLANE_COMPONENTS) {
+            if (layout.sampleIncrementInBits != 16) {
+                return BAD_VALUE;
+            }
+            if (layout.components[0].offsetInBits != 6) {
+                return BAD_VALUE;
+            }
+            dataY = (uint8_t *)pData + layout.offsetInBytes;
+            strideY = layout.strideInBytes;
+        } else if (components == CBCR_PLANE_COMPONENTS) {
+            if (layout.sampleIncrementInBits != 32) {
+                return BAD_VALUE;
+            }
+            for (const PlaneLayoutComponent &component : layout.components) {
+                if (component.type.value == int64_t(PlaneLayoutComponentType::CB) &&
+                    component.offsetInBits != 6) {
+                    return BAD_VALUE;
+                }
+                if (component.type.value == int64_t(PlaneLayoutComponentType::CR) &&
+                    component.offsetInBits != 22) {
+                    return BAD_VALUE;
+                }
+            }
+            dataCb = (uint8_t *)pData + layout.offsetInBytes;
+            dataCr = (uint8_t *)pData + layout.offsetInBytes + 2;
+            strideCbCr = layout.strideInBytes;
+        } else {
+            return BAD_VALUE;
+        }
+    }
+
+    outputImage->data = dataY;
+    outputImage->width = buffer->getWidth();
+    outputImage->height = buffer->getHeight();
+    outputImage->format = format;
+    outputImage->flexFormat =
+            static_cast<int>(AidlPixelFormat::YCBCR_P210);
+    outputImage->stride = strideY;
+
+    outputImage->dataCb = dataCb;
+    outputImage->dataCr = dataCr;
+    outputImage->chromaStride = strideCbCr;
+    outputImage->chromaStep = 4;
+    return OK;
+}
+
 status_t lockImageFromBuffer(sp<GraphicBuffer> buffer, uint32_t inUsage,
         const Rect& rect, int fenceFd, LockedImage* outputImage) {
     ALOGV("%s: Try to lock the GraphicBuffer", __FUNCTION__);
@@ -581,10 +701,17 @@
             ALOGE("Lock buffer failed!");
             return res;
         }
-        if (isPossibly10BitYUV(format)
-                && OK == extractP010Gralloc4PlaneLayout(buffer, pData, format, outputImage)) {
-            ALOGV("%s: Successfully locked the P010 image", __FUNCTION__);
-            return OK;
+        if (isPossibly10BitYUV(format)) {
+            if (format == HAL_PIXEL_FORMAT_YCBCR_P010 &&
+                OK == extractP010Gralloc4PlaneLayout(buffer, pData, format, outputImage)) {
+                ALOGV("%s: Successfully locked the P010 image", __FUNCTION__);
+                return OK;
+            } else if ((format ==
+                        static_cast<int>(AidlPixelFormat::YCBCR_P210)) &&
+                       OK == extractP210Gralloc4PlaneLayout(buffer, pData, format, outputImage)) {
+                ALOGV("%s: Successfully locked the P210 image", __FUNCTION__);
+                return OK;
+            }
         }
     }
 
diff --git a/native/android/dynamic_instrumentation_manager.cpp b/native/android/dynamic_instrumentation_manager.cpp
index 0749731..ee2cd6f 100644
--- a/native/android/dynamic_instrumentation_manager.cpp
+++ b/native/android/dynamic_instrumentation_manager.cpp
@@ -73,6 +73,7 @@
 
 void ADynamicInstrumentationManager_TargetProcess_destroy(
         const ADynamicInstrumentationManager_TargetProcess* instance) {
+    if (instance == nullptr) return;
     delete instance;
 }
 
@@ -104,6 +105,7 @@
 
 void ADynamicInstrumentationManager_MethodDescriptor_destroy(
         const ADynamicInstrumentationManager_MethodDescriptor* instance) {
+    if (instance == nullptr) return;
     delete instance;
 }
 
@@ -135,6 +137,7 @@
 
 void ADynamicInstrumentationManager_ExecutableMethodFileOffsets_destroy(
         const ADynamicInstrumentationManager_ExecutableMethodFileOffsets* instance) {
+    if (instance == nullptr) return;
     delete instance;
 }
 
diff --git a/native/android/include_platform/android/dynamic_instrumentation_manager.h b/native/android/include_platform/android/dynamic_instrumentation_manager.h
index ab9f370..7bb7615 100644
--- a/native/android/include_platform/android/dynamic_instrumentation_manager.h
+++ b/native/android/include_platform/android/dynamic_instrumentation_manager.h
@@ -40,9 +40,12 @@
  *
  * @param uid of targeted process.
  * @param pid of targeted process.
- * @param processName to disambiguate from corner cases that may arise from pid reuse.
+ * @param processName UTF-8 encoded string representing the same process as specified by `pid`.
+ *                    Supplied to disambiguate from corner cases that may arise from pid reuse.
+ *                    Referenced parameter must outlive the returned
+ *                    ADynamicInstrumentationManager_TargetProcess.
  */
-ADynamicInstrumentationManager_TargetProcess* _Nonnull
+ADynamicInstrumentationManager_TargetProcess* _Nullable
         ADynamicInstrumentationManager_TargetProcess_create(
                 uid_t uid, pid_t pid, const char* _Nonnull processName) __INTRODUCED_IN(36);
 /**
@@ -51,22 +54,27 @@
  * @param instance returned from ADynamicInstrumentationManager_TargetProcess_create.
  */
 void ADynamicInstrumentationManager_TargetProcess_destroy(
-        const ADynamicInstrumentationManager_TargetProcess* _Nonnull instance) __INTRODUCED_IN(36);
+        const ADynamicInstrumentationManager_TargetProcess* _Nullable instance) __INTRODUCED_IN(36);
 
 /**
  * Initializes an ADynamicInstrumentationManager_MethodDescriptor. Caller must clean up when they
- * are done with ADynamicInstrumentationManager_MethodDescriptor_Destroy.
+ * are done with ADynamicInstrumentationManager_MethodDescriptor_destroy.
  *
- * @param fullyQualifiedClassName fqcn of class containing the method.
- * @param methodName
- * @param fullyQualifiedParameters fqcn of parameters of the method's signature, or e.g. "int" for
- *                                 primitives.
+ * @param fullyQualifiedClassName UTF-8 encoded fqcn of class containing the method. Referenced
+ *                                parameter must outlive the returned
+ *                                ADynamicInstrumentationManager_MethodDescriptor.
+ * @param methodName UTF-8 encoded method name. Referenced parameter must outlive the returned
+ *                   ADynamicInstrumentationManager_MethodDescriptor.
+ * @param fullyQualifiedParameters UTF-8 encoded fqcn of parameters of the method's signature,
+ *                                 or e.g. "int" for primitives. Referenced parameter should
+ *                                 outlive the returned
+ *                                 ADynamicInstrumentationManager_MethodDescriptor.
  * @param numParameters length of `fullyQualifiedParameters` array.
  */
-ADynamicInstrumentationManager_MethodDescriptor* _Nonnull
+ADynamicInstrumentationManager_MethodDescriptor* _Nullable
         ADynamicInstrumentationManager_MethodDescriptor_create(
                 const char* _Nonnull fullyQualifiedClassName, const char* _Nonnull methodName,
-                const char* _Nonnull fullyQualifiedParameters[_Nonnull], size_t numParameters)
+                const char* _Nonnull* _Nonnull fullyQualifiedParameters, size_t numParameters)
                 __INTRODUCED_IN(36);
 /**
  * Clean up an ADynamicInstrumentationManager_MethodDescriptor.
@@ -74,14 +82,16 @@
  * @param instance returned from ADynamicInstrumentationManager_MethodDescriptor_create.
  */
 void ADynamicInstrumentationManager_MethodDescriptor_destroy(
-        const ADynamicInstrumentationManager_MethodDescriptor* _Nonnull instance)
+        const ADynamicInstrumentationManager_MethodDescriptor* _Nullable instance)
         __INTRODUCED_IN(36);
 
 /**
  * Get the containerPath calculated by
  * ADynamicInstrumentationManager_getExecutableMethodFileOffsets.
  * @param instance created with ADynamicInstrumentationManager_getExecutableMethodFileOffsets.
- * @return The OS path of the containing file.
+ * @return The OS path of the containing file as a UTF-8 string, which has the same lifetime
+ *         as the ADynamicInstrumentationManager_ExecutableMethodFileOffsets instance passed
+ *         as a param.
  */
 const char* _Nullable ADynamicInstrumentationManager_ExecutableMethodFileOffsets_getContainerPath(
         const ADynamicInstrumentationManager_ExecutableMethodFileOffsets* _Nonnull instance)
@@ -90,7 +100,8 @@
  * Get the containerOffset calculated by
  * ADynamicInstrumentationManager_getExecutableMethodFileOffsets.
  * @param instance created with ADynamicInstrumentationManager_getExecutableMethodFileOffsets.
- * @return The offset of the containing file within the process' memory.
+ * @return The absolute address of the containing file within remote the process' virtual memory
+ *         space.
  */
 uint64_t ADynamicInstrumentationManager_ExecutableMethodFileOffsets_getContainerOffset(
         const ADynamicInstrumentationManager_ExecutableMethodFileOffsets* _Nonnull instance)
@@ -98,7 +109,8 @@
 /**
  * Get the methodOffset calculated by ADynamicInstrumentationManager_getExecutableMethodFileOffsets.
  * @param instance created with ADynamicInstrumentationManager_getExecutableMethodFileOffsets.
- * @return The offset of the method within the containing file.
+ * @return The offset of the method within the container whose address is returned by
+ *         ADynamicInstrumentationManager_ExecutableMethodFileOffsets_getContainerOffset.
  */
 uint64_t ADynamicInstrumentationManager_ExecutableMethodFileOffsets_getMethodOffset(
         const ADynamicInstrumentationManager_ExecutableMethodFileOffsets* _Nonnull instance)
@@ -109,7 +121,7 @@
  * @param instance returned from ADynamicInstrumentationManager_getExecutableMethodFileOffsets.
  */
 void ADynamicInstrumentationManager_ExecutableMethodFileOffsets_destroy(
-        const ADynamicInstrumentationManager_ExecutableMethodFileOffsets* _Nonnull instance)
+        const ADynamicInstrumentationManager_ExecutableMethodFileOffsets* _Nullable instance)
         __INTRODUCED_IN(36);
 /**
  * Provides ART metadata about the described java method within the target process.
@@ -118,7 +130,9 @@
  * @param methodDescriptor describes the targeted method.
  * @param out will be populated with the data if successful. A nullptr combined
  *        with an OK status means that the program method is defined, but the offset
- *        info was unavailable because it is not AOT compiled.
+ *        info was unavailable because it is not AOT compiled. Caller owns `out` and
+ *        should clean it up with
+ *        ADynamicInstrumentationManager_ExecutableMethodFileOffsets_destroy.
  * @return status indicating success or failure. The values correspond to the `binder_exception_t`
  *         enum values from <android/binder_status.h>.
  */
diff --git a/nfc-non-updatable/Android.bp b/nfc-non-updatable/Android.bp
new file mode 100644
index 0000000..ff987bb
--- /dev/null
+++ b/nfc-non-updatable/Android.bp
@@ -0,0 +1,23 @@
+package {
+    default_team: "trendy_team_fwk_nfc",
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+filegroup {
+    name: "framework-nfc-non-updatable-sources",
+    path: "java",
+    srcs: [
+        "java/android/nfc/NfcServiceManager.java",
+        "java/android/nfc/cardemulation/ApduServiceInfo.aidl",
+        "java/android/nfc/cardemulation/ApduServiceInfo.java",
+        "java/android/nfc/cardemulation/NfcFServiceInfo.aidl",
+        "java/android/nfc/cardemulation/NfcFServiceInfo.java",
+        "java/android/nfc/cardemulation/AidGroup.aidl",
+        "java/android/nfc/cardemulation/AidGroup.java",
+    ],
+}
diff --git a/nfc/java/android/nfc/cardemulation/OWNERS b/nfc-non-updatable/OWNERS
similarity index 98%
rename from nfc/java/android/nfc/cardemulation/OWNERS
rename to nfc-non-updatable/OWNERS
index 35e9713..f46dccd 100644
--- a/nfc/java/android/nfc/cardemulation/OWNERS
+++ b/nfc-non-updatable/OWNERS
@@ -1,2 +1,2 @@
 # Bug component: 48448
-include platform/packages/apps/Nfc:/OWNERS
+include platform/packages/apps/Nfc:/OWNERS
\ No newline at end of file
diff --git a/nfc/java/android/nfc/flags.aconfig b/nfc-non-updatable/flags/flags.aconfig
similarity index 100%
rename from nfc/java/android/nfc/flags.aconfig
rename to nfc-non-updatable/flags/flags.aconfig
diff --git a/nfc/java/android/nfc/NfcServiceManager.java b/nfc-non-updatable/java/android/nfc/NfcServiceManager.java
similarity index 100%
rename from nfc/java/android/nfc/NfcServiceManager.java
rename to nfc-non-updatable/java/android/nfc/NfcServiceManager.java
diff --git a/nfc/java/android/nfc/cardemulation/AidGroup.aidl b/nfc-non-updatable/java/android/nfc/cardemulation/AidGroup.aidl
similarity index 100%
rename from nfc/java/android/nfc/cardemulation/AidGroup.aidl
rename to nfc-non-updatable/java/android/nfc/cardemulation/AidGroup.aidl
diff --git a/nfc/java/android/nfc/cardemulation/AidGroup.java b/nfc-non-updatable/java/android/nfc/cardemulation/AidGroup.java
similarity index 100%
rename from nfc/java/android/nfc/cardemulation/AidGroup.java
rename to nfc-non-updatable/java/android/nfc/cardemulation/AidGroup.java
diff --git a/nfc/java/android/nfc/cardemulation/ApduServiceInfo.aidl b/nfc-non-updatable/java/android/nfc/cardemulation/ApduServiceInfo.aidl
similarity index 100%
rename from nfc/java/android/nfc/cardemulation/ApduServiceInfo.aidl
rename to nfc-non-updatable/java/android/nfc/cardemulation/ApduServiceInfo.aidl
diff --git a/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java b/nfc-non-updatable/java/android/nfc/cardemulation/ApduServiceInfo.java
similarity index 98%
rename from nfc/java/android/nfc/cardemulation/ApduServiceInfo.java
rename to nfc-non-updatable/java/android/nfc/cardemulation/ApduServiceInfo.java
index 308b5d1..7f64dbe 100644
--- a/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java
+++ b/nfc-non-updatable/java/android/nfc/cardemulation/ApduServiceInfo.java
@@ -174,7 +174,7 @@
      * Whether or not this service wants to share the same routing priority as the
      * Wallet role owner.
      */
-    private boolean mShareRolePriority;
+    private boolean mWantsRoleHolderPriority;
 
     /**
      * @hide
@@ -314,8 +314,8 @@
                         R.styleable.HostApduService_shouldDefaultToObserveMode,
                         false);
                 if (Flags.nfcAssociatedRoleServices()) {
-                    mShareRolePriority = sa.getBoolean(
-                            R.styleable.HostApduService_shareRolePriority,
+                    mWantsRoleHolderPriority = sa.getBoolean(
+                            R.styleable.HostApduService_wantsRoleHolderPriority,
                             false
                     );
                 }
@@ -350,8 +350,8 @@
                 }
                 mStaticOffHostName = mOffHostName;
                 if (Flags.nfcAssociatedRoleServices()) {
-                    mShareRolePriority = sa.getBoolean(
-                            R.styleable.OffHostApduService_shareRolePriority,
+                    mWantsRoleHolderPriority = sa.getBoolean(
+                            R.styleable.OffHostApduService_wantsRoleHolderPriority,
                             false
                     );
                 }
@@ -752,8 +752,8 @@
      * @return whether or not this service wants to share priority
      */
     @FlaggedApi(Flags.FLAG_NFC_ASSOCIATED_ROLE_SERVICES)
-    public boolean shareRolePriority() {
-        return mShareRolePriority;
+    public boolean wantsRoleHolderPriority() {
+        return mWantsRoleHolderPriority;
     }
 
     /**
diff --git a/nfc/java/android/nfc/cardemulation/NfcFServiceInfo.aidl b/nfc-non-updatable/java/android/nfc/cardemulation/NfcFServiceInfo.aidl
similarity index 100%
rename from nfc/java/android/nfc/cardemulation/NfcFServiceInfo.aidl
rename to nfc-non-updatable/java/android/nfc/cardemulation/NfcFServiceInfo.aidl
diff --git a/nfc/java/android/nfc/cardemulation/NfcFServiceInfo.java b/nfc-non-updatable/java/android/nfc/cardemulation/NfcFServiceInfo.java
similarity index 100%
rename from nfc/java/android/nfc/cardemulation/NfcFServiceInfo.java
rename to nfc-non-updatable/java/android/nfc/cardemulation/NfcFServiceInfo.java
diff --git a/nfc/Android.bp b/nfc/Android.bp
index abe0ab7..0fdb3bd 100644
--- a/nfc/Android.bp
+++ b/nfc/Android.bp
@@ -9,28 +9,16 @@
 }
 
 filegroup {
-    name: "framework-nfc-non-updatable-sources",
-    path: "java",
-    srcs: [
-        "java/android/nfc/NfcServiceManager.java",
-        "java/android/nfc/cardemulation/ApduServiceInfo.aidl",
-        "java/android/nfc/cardemulation/ApduServiceInfo.java",
-        "java/android/nfc/cardemulation/NfcFServiceInfo.aidl",
-        "java/android/nfc/cardemulation/NfcFServiceInfo.java",
-        "java/android/nfc/cardemulation/AidGroup.aidl",
-        "java/android/nfc/cardemulation/AidGroup.java",
-    ],
-}
-
-filegroup {
     name: "framework-nfc-updatable-sources",
     path: "java",
     srcs: [
         "java/**/*.java",
         "java/**/*.aidl",
     ],
-    exclude_srcs: [
-        ":framework-nfc-non-updatable-sources",
+    visibility: [
+        "//frameworks/base:__subpackages__",
+        "//packages/apps/Nfc:__subpackages__",
+        "//packages/modules/Nfc:__subpackages__",
     ],
 }
 
@@ -56,7 +44,7 @@
     ],
     defaults: ["framework-module-defaults"],
     sdk_version: "module_current",
-    min_sdk_version: "current",
+    min_sdk_version: "35", // Make it 36 once available.
     installable: true,
     optimize: {
         enabled: false,
@@ -68,9 +56,9 @@
     ],
     impl_library_visibility: [
         "//frameworks/base:__subpackages__",
-        "//cts/hostsidetests/multidevices/nfc:__subpackages__",
-        "//cts/tests/tests/nfc",
+        "//cts:__subpackages__",
         "//packages/apps/Nfc:__subpackages__",
+        "//packages/modules/Nfc:__subpackages__",
     ],
     jarjar_rules: ":nfc-jarjar-rules",
     lint: {
diff --git a/nfc/OWNERS b/nfc/OWNERS
index 35e9713..f46dccd 100644
--- a/nfc/OWNERS
+++ b/nfc/OWNERS
@@ -1,2 +1,2 @@
 # Bug component: 48448
-include platform/packages/apps/Nfc:/OWNERS
+include platform/packages/apps/Nfc:/OWNERS
\ No newline at end of file
diff --git a/nfc/api/current.txt b/nfc/api/current.txt
index 0ee81cb..c8c479a 100644
--- a/nfc/api/current.txt
+++ b/nfc/api/current.txt
@@ -211,7 +211,7 @@
     method public boolean isDefaultServiceForCategory(android.content.ComponentName, String);
     method @FlaggedApi("android.nfc.enable_card_emulation_euicc") public boolean isEuiccSupported();
     method public boolean registerAidsForService(android.content.ComponentName, String, java.util.List<java.lang.String>);
-    method @FlaggedApi("android.nfc.nfc_event_listener") public void registerNfcEventListener(@NonNull java.util.concurrent.Executor, @NonNull android.nfc.cardemulation.CardEmulation.NfcEventListener);
+    method @FlaggedApi("android.nfc.nfc_event_listener") public void registerNfcEventCallback(@NonNull java.util.concurrent.Executor, @NonNull android.nfc.cardemulation.CardEmulation.NfcEventCallback);
     method @FlaggedApi("android.nfc.nfc_read_polling_loop") public boolean registerPollingLoopFilterForService(@NonNull android.content.ComponentName, @NonNull String, boolean);
     method @FlaggedApi("android.nfc.nfc_read_polling_loop") public boolean registerPollingLoopPatternFilterForService(@NonNull android.content.ComponentName, @NonNull String, boolean);
     method public boolean removeAidsForService(android.content.ComponentName, String);
@@ -221,7 +221,7 @@
     method public boolean setPreferredService(android.app.Activity, android.content.ComponentName);
     method @FlaggedApi("android.nfc.nfc_observe_mode") public boolean setShouldDefaultToObserveModeForService(@NonNull android.content.ComponentName, boolean);
     method public boolean supportsAidPrefixRegistration();
-    method @FlaggedApi("android.nfc.nfc_event_listener") public void unregisterNfcEventListener(@NonNull android.nfc.cardemulation.CardEmulation.NfcEventListener);
+    method @FlaggedApi("android.nfc.nfc_event_listener") public void unregisterNfcEventCallback(@NonNull android.nfc.cardemulation.CardEmulation.NfcEventCallback);
     method @NonNull @RequiresPermission(android.Manifest.permission.NFC) public boolean unsetOffHostForService(@NonNull android.content.ComponentName);
     method public boolean unsetPreferredService(android.app.Activity);
     field @Deprecated public static final String ACTION_CHANGE_DEFAULT = "android.nfc.cardemulation.action.ACTION_CHANGE_DEFAULT";
@@ -244,7 +244,7 @@
     field public static final int SELECTION_MODE_PREFER_DEFAULT = 0; // 0x0
   }
 
-  @FlaggedApi("android.nfc.nfc_event_listener") public static interface CardEmulation.NfcEventListener {
+  @FlaggedApi("android.nfc.nfc_event_listener") public static interface CardEmulation.NfcEventCallback {
     method @FlaggedApi("android.nfc.nfc_event_listener") public default void onAidConflictOccurred(@NonNull String);
     method @FlaggedApi("android.nfc.nfc_event_listener") public default void onAidNotRouted(@NonNull String);
     method @FlaggedApi("android.nfc.nfc_event_listener") public default void onInternalErrorReported(int);
diff --git a/nfc/api/system-current.txt b/nfc/api/system-current.txt
index cf11ac0..6bd6072 100644
--- a/nfc/api/system-current.txt
+++ b/nfc/api/system-current.txt
@@ -200,9 +200,11 @@
     method @Nullable @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) @WorkerThread public android.nfc.T4tNdefNfceeCcFileInfo readCcfile();
     method @NonNull @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) @WorkerThread public byte[] readData(@IntRange(from=0, to=65535) int);
     method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) @WorkerThread public int writeData(@IntRange(from=0, to=65535) int, @NonNull byte[]);
+    field public static final int CLEAR_DATA_FAILED_DEVICE_BUSY = -1; // 0xffffffff
     field public static final int CLEAR_DATA_FAILED_INTERNAL = 0; // 0x0
     field public static final int CLEAR_DATA_SUCCESS = 1; // 0x1
     field public static final int WRITE_DATA_ERROR_CONNECTION_FAILED = -6; // 0xfffffffa
+    field public static final int WRITE_DATA_ERROR_DEVICE_BUSY = -9; // 0xfffffff7
     field public static final int WRITE_DATA_ERROR_EMPTY_PAYLOAD = -7; // 0xfffffff9
     field public static final int WRITE_DATA_ERROR_INTERNAL = -1; // 0xffffffff
     field public static final int WRITE_DATA_ERROR_INVALID_FILE_ID = -4; // 0xfffffffc
@@ -217,21 +219,14 @@
     method public int describeContents();
     method @IntRange(from=15, to=32767) public int getCcFileLength();
     method @IntRange(from=0xffffffff, to=65535) public int getFileId();
-    method @IntRange(from=15, to=65535) public int getMaxReadLength();
     method @IntRange(from=5, to=32767) public int getMaxSize();
-    method @IntRange(from=13, to=65535) public int getMaxWriteLength();
-    method public int getReadAccess();
     method public int getVersion();
-    method public int getWriteAccess();
+    method public boolean isReadAllowed();
+    method public boolean isWriteAllowed();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.nfc.T4tNdefNfceeCcFileInfo> CREATOR;
-    field public static final int READ_ACCESS_GRANTED_RESTRICTED = 128; // 0x80
-    field public static final int READ_ACCESS_GRANTED_UNRESTRICTED = 0; // 0x0
     field public static final int VERSION_2_0 = 32; // 0x20
     field public static final int VERSION_3_0 = 48; // 0x30
-    field public static final int WRITE_ACCESS_GRANTED_RESTRICTED = 128; // 0x80
-    field public static final int WRITE_ACCESS_GRANTED_UNRESTRICTED = 0; // 0x0
-    field public static final int WRITE_ACCESS_NOT_GRANTED = 255; // 0xff
   }
 
 }
diff --git a/nfc/java/android/nfc/INfcCardEmulation.aidl b/nfc/java/android/nfc/INfcCardEmulation.aidl
index bb9fe95..00ceaa9 100644
--- a/nfc/java/android/nfc/INfcCardEmulation.aidl
+++ b/nfc/java/android/nfc/INfcCardEmulation.aidl
@@ -17,7 +17,7 @@
 package android.nfc;
 
 import android.content.ComponentName;
-import android.nfc.INfcEventListener;
+import android.nfc.INfcEventCallback;
 
 import android.nfc.cardemulation.AidGroup;
 import android.nfc.cardemulation.ApduServiceInfo;
@@ -60,6 +60,6 @@
     List<String> getRoutingStatus();
     void overwriteRoutingTable(int userHandle, String emptyAid, String protocol, String tech, String sc);
 
-    void registerNfcEventListener(in INfcEventListener listener);
-    void unregisterNfcEventListener(in INfcEventListener listener);
+    void registerNfcEventCallback(in INfcEventCallback listener);
+    void unregisterNfcEventCallback(in INfcEventCallback listener);
 }
diff --git a/nfc/java/android/nfc/INfcEventListener.aidl b/nfc/java/android/nfc/INfcEventCallback.aidl
similarity index 92%
rename from nfc/java/android/nfc/INfcEventListener.aidl
rename to nfc/java/android/nfc/INfcEventCallback.aidl
index 774d8f8..af1fa2fb 100644
--- a/nfc/java/android/nfc/INfcEventListener.aidl
+++ b/nfc/java/android/nfc/INfcEventCallback.aidl
@@ -5,7 +5,7 @@
 /**
  * @hide
  */
-oneway interface INfcEventListener {
+oneway interface INfcEventCallback {
     void onPreferredServiceChanged(in ComponentNameAndUser ComponentNameAndUser);
     void onObserveModeStateChanged(boolean isEnabled);
     void onAidConflictOccurred(in String aid);
diff --git a/nfc/java/android/nfc/NfcAdapter.java b/nfc/java/android/nfc/NfcAdapter.java
index 89ce423..63397c2 100644
--- a/nfc/java/android/nfc/NfcAdapter.java
+++ b/nfc/java/android/nfc/NfcAdapter.java
@@ -1789,6 +1789,11 @@
      * @param listenTechnology Flags indicating listen technologies.
      * @throws UnsupportedOperationException if FEATURE_NFC,
      * FEATURE_NFC_HOST_CARD_EMULATION, FEATURE_NFC_HOST_CARD_EMULATION_NFCF are unavailable.
+     *
+     * NOTE: This API overrides all technology flags regardless of the current device state,
+     *       it is incompatible with enableReaderMode() API and the others that either update
+     *       or assume any techlology flag set by the OS.
+     *       Please use with care.
      */
 
     @FlaggedApi(Flags.FLAG_ENABLE_NFC_SET_DISCOVERY_TECH)
diff --git a/nfc/java/android/nfc/T4tNdefNfcee.java b/nfc/java/android/nfc/T4tNdefNfcee.java
index 06d02c5..05a30aa 100644
--- a/nfc/java/android/nfc/T4tNdefNfcee.java
+++ b/nfc/java/android/nfc/T4tNdefNfcee.java
@@ -100,9 +100,14 @@
     public static final int WRITE_DATA_ERROR_EMPTY_PAYLOAD = -7;
     /**
      * Returns flag for {@link #writeData(int, byte[])}.
-     * It idicates write data fail due to invalid ndef format.
+     * It indicates write data fail due to invalid ndef format.
      */
     public static final int WRITE_DATA_ERROR_NDEF_VALIDATION_FAILED = -8;
+    /**
+     * Returns flag for {@link #writeData(int, byte[])}.
+     * It indicates write data fail if a concurrent NDEF NFCEE operation is ongoing.
+     */
+    public static final int WRITE_DATA_ERROR_DEVICE_BUSY = -9;
 
     /**
      * Possible return values for {@link #writeData(int, byte[])}.
@@ -119,6 +124,7 @@
         WRITE_DATA_ERROR_CONNECTION_FAILED,
         WRITE_DATA_ERROR_EMPTY_PAYLOAD,
         WRITE_DATA_ERROR_NDEF_VALIDATION_FAILED,
+        WRITE_DATA_ERROR_DEVICE_BUSY,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface WriteDataStatus{}
@@ -128,6 +134,9 @@
      *
      * <p>This is an I/O operation and will block until complete. It must
      * not be called from the main application thread.</p>
+     * <p>Applications must send complete Ndef Message payload, do not need to fragment
+     * the payload, it will be automatically fragmented and defragmented by
+     * {@link #writeData} if it exceeds max message length limits</p>
      *
      * @param fileId File id (Refer NFC Forum Type 4 Tag Specification
      *               Section 4.2 File Identifiers and Access Conditions
@@ -155,9 +164,10 @@
      * @param fileId File Id (Refer
      *               Section 4.2 File Identifiers and Access Conditions
      *               for more information) from which to read.
-     * @return - Returns Ndef message if success
+     * @return - Returns complete Ndef message if success
      *           Refer to Nfc forum NDEF specification NDEF Message section
-     * @throws IllegalStateException if read fails because the fileId is invalid.
+     * @throws IllegalStateException if read fails because the fileId is invalid
+     *         or if a concurrent operation is in progress.
      * @hide
      */
     @SystemApi
@@ -179,6 +189,12 @@
      * It indicates clear data failed due to internal error while processing the clear.
      */
     public static final int CLEAR_DATA_FAILED_INTERNAL = 0;
+    /**
+     * Return flag for {@link #clearNdefData()}.
+     * It indicates clear data failed  if a concurrent NDEF NFCEE operation is ongoing.
+     */
+    public static final int CLEAR_DATA_FAILED_DEVICE_BUSY = -1;
+
 
     /**
      * Possible return values for {@link #clearNdefData()}.
@@ -188,6 +204,7 @@
     @IntDef(prefix = { "CLEAR_DATA_" }, value = {
         CLEAR_DATA_SUCCESS,
         CLEAR_DATA_FAILED_INTERNAL,
+        CLEAR_DATA_FAILED_DEVICE_BUSY,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface ClearDataStatus{}
@@ -245,6 +262,7 @@
      * Refer to the NFC forum specification "NFCForum-TS-T4T-1.1 section 4.4" for more details.
      *
      * @return Returns CC file content if success or null if failed to read.
+     * @throws IllegalStateException if the device is busy.
      * @hide
      */
     @SystemApi
diff --git a/nfc/java/android/nfc/T4tNdefNfceeCcFileInfo.java b/nfc/java/android/nfc/T4tNdefNfceeCcFileInfo.java
index 5fca052..ce67f8f 100644
--- a/nfc/java/android/nfc/T4tNdefNfceeCcFileInfo.java
+++ b/nfc/java/android/nfc/T4tNdefNfceeCcFileInfo.java
@@ -47,14 +47,6 @@
      */
     private int mVersion;
     /**
-     * Indicates the max data size by a single ReadBinary<p>
-     */
-    private int mMaxReadLength;
-    /**
-     * Indicates the max data size by a single UpdateBinary<p>
-     */
-    private int mMaxWriteLength;
-    /**
      * Indicates the NDEF File Identifier<p>
      */
     private int mFileId;
@@ -65,40 +57,35 @@
     /**
      * Indicates the read access condition<p>
      */
-    private int mReadAccess;
+    private boolean mIsReadAllowed;
     /**
      * Indicates the write access condition<p>
      */
-    private int mWriteAccess;
+    private boolean mIsWriteAllowed;
 
     /**
      * Constructor to be used by NFC service and internal classes.
      * @hide
      */
-    public T4tNdefNfceeCcFileInfo(int cclen, int version, int maxLe, int maxLc,
+    public T4tNdefNfceeCcFileInfo(int cclen, int version,
                       int ndefFileId, int ndefMaxSize,
-                      int ndefReadAccess, int ndefWriteAccess) {
+                      boolean isReadAllowed, boolean isWriteAllowed) {
         mCcLength = cclen;
         mVersion = version;
-        mMaxWriteLength = maxLc;
-        mMaxReadLength = maxLe;
         mFileId = ndefFileId;
         mMaxSize = ndefMaxSize;
-        mReadAccess = ndefReadAccess;
-        mWriteAccess = ndefWriteAccess;
+        mIsReadAllowed = isReadAllowed;
+        mIsWriteAllowed = isWriteAllowed;
     }
 
     @Override
     public void writeToParcel(@NonNull Parcel dest, int flags) {
-
         dest.writeInt(mCcLength);
         dest.writeInt(mVersion);
-        dest.writeInt(mMaxWriteLength);
-        dest.writeInt(mMaxReadLength);
         dest.writeInt(mFileId);
         dest.writeInt(mMaxSize);
-        dest.writeInt(mReadAccess);
-        dest.writeInt(mWriteAccess);
+        dest.writeBoolean(mIsReadAllowed);
+        dest.writeBoolean(mIsWriteAllowed);
     }
 
     /**
@@ -146,30 +133,6 @@
     }
 
     /**
-     * Indicates the max data size that can be read by a single invocation of
-     * {@link T4tNdefNfcee#readData(int)}.
-     *
-     * Refer to the NFC forum specification "NFCForum-TS-T4T-1.1 section 4.4" MLe.
-     * @return max size of read (in bytes).
-     */
-    @IntRange(from = 0xf, to = 0xffff)
-    public int getMaxReadLength() {
-        return mMaxReadLength;
-    }
-
-    /**
-     * Indicates the max data size that can be written by a single invocation of
-     * {@link T4tNdefNfcee#writeData(int, byte[])}
-     *
-     * Refer to the NFC forum specification "NFCForum-TS-T4T-1.1 section 4.4" MLc.
-     * @return max size of write (in bytes).
-     */
-    @IntRange(from = 0xd, to = 0xffff)
-    public int getMaxWriteLength() {
-        return mMaxWriteLength;
-    }
-
-    /**
      * Indicates the NDEF File Identifier. This is the identifier used in the last invocation of
      * {@link T4tNdefNfcee#writeData(int, byte[])}
      *
@@ -191,73 +154,21 @@
     }
 
     /**
-     * T4T tag read access granted without any security.
-     * Refer to the NFC forum specification "NFCForum-TS-T4T-1.1 section 4.2" for more details.
-     */
-    public static final int READ_ACCESS_GRANTED_UNRESTRICTED = 0x0;
-    /**
-     * T4T tag read access granted with limited proprietary access only.
-     * Refer to the NFC forum specification "NFCForum-TS-T4T-1.1 section 4.2" for more details.
-     */
-    public static final int READ_ACCESS_GRANTED_RESTRICTED = 0x80;
-
-    /**
-     * Possible return values for {@link #getVersion()}.
-     * @hide
-     */
-    @IntDef(prefix = { "READ_ACCESS_GRANTED_" }, value = {
-            READ_ACCESS_GRANTED_RESTRICTED,
-            READ_ACCESS_GRANTED_UNRESTRICTED,
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface ReadAccess {}
-
-    /**
      * Indicates the read access condition.
      * Refer to the NFC forum specification "NFCForum-TS-T4T-1.1 section 4.2" for more details.
-     * @return read access restriction
+     * @return boolean true if read access is allowed, otherwise false.
      */
-    @ReadAccess
-    public int getReadAccess() {
-        return mReadAccess;
+    public boolean isReadAllowed() {
+        return mIsReadAllowed;
     }
 
     /**
-     * T4T tag write access granted without any security.
-     * Refer to the NFC forum specification "NFCForum-TS-T4T-1.1 section 4.2" for more details.
-     */
-    public static final int WRITE_ACCESS_GRANTED_UNRESTRICTED = 0x0;
-    /**
-     * T4T tag write access granted with limited proprietary access only.
-     * Refer to the NFC forum specification "NFCForum-TS-T4T-1.1 section 4.2" for more details.
-     */
-    public static final int WRITE_ACCESS_GRANTED_RESTRICTED = 0x80;
-    /**
-     * T4T tag write access not granted.
-     * Refer to the NFC forum specification "NFCForum-TS-T4T-1.1 section 4.2" for more details.
-     */
-    public static final int WRITE_ACCESS_NOT_GRANTED = 0xFF;
-
-    /**
-     * Possible return values for {@link #getVersion()}.
-     * @hide
-     */
-    @IntDef(prefix = { "READ_ACCESS_GRANTED_" }, value = {
-            WRITE_ACCESS_GRANTED_RESTRICTED,
-            WRITE_ACCESS_GRANTED_UNRESTRICTED,
-            WRITE_ACCESS_NOT_GRANTED,
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface WriteAccess {}
-
-    /**
      * Indicates the write access condition.
      * Refer to the NFC forum specification "NFCForum-TS-T4T-1.1 section 4.2" for more details.
-     * @return write access restriction
+     * @return boolean if write access is allowed, otherwise false.
      */
-    @WriteAccess
-    public int getWriteAccess() {
-        return mWriteAccess;
+    public boolean isWriteAllowed() {
+        return mIsWriteAllowed;
     }
 
     @Override
@@ -273,16 +184,14 @@
                     // NdefNfceeCcFileInfo fields
                     int cclen = in.readInt();
                     int version = in.readInt();
-                    int maxLe = in.readInt();
-                    int maxLc = in.readInt();
                     int ndefFileId = in.readInt();
                     int ndefMaxSize = in.readInt();
-                    int ndefReadAccess = in.readInt();
-                    int ndefWriteAccess = in.readInt();
+                    boolean isReadAllowed = in.readBoolean();
+                    boolean isWriteAllowed = in.readBoolean();
 
-                    return new T4tNdefNfceeCcFileInfo(cclen, version, maxLe, maxLc,
+                    return new T4tNdefNfceeCcFileInfo(cclen, version,
                             ndefFileId, ndefMaxSize,
-                            ndefReadAccess, ndefWriteAccess);
+                            isReadAllowed, isWriteAllowed);
                 }
 
                 @Override
diff --git a/nfc/java/android/nfc/cardemulation/CardEmulation.java b/nfc/java/android/nfc/cardemulation/CardEmulation.java
index e0bc15f..fee9c5b 100644
--- a/nfc/java/android/nfc/cardemulation/CardEmulation.java
+++ b/nfc/java/android/nfc/cardemulation/CardEmulation.java
@@ -39,7 +39,7 @@
 import android.nfc.Constants;
 import android.nfc.Flags;
 import android.nfc.INfcCardEmulation;
-import android.nfc.INfcEventListener;
+import android.nfc.INfcEventCallback;
 import android.nfc.NfcAdapter;
 import android.os.Build;
 import android.os.RemoteException;
@@ -1304,7 +1304,7 @@
 
     /** Listener for preferred service state changes. */
     @FlaggedApi(android.nfc.Flags.FLAG_NFC_EVENT_LISTENER)
-    public interface NfcEventListener {
+    public interface NfcEventCallback {
         /**
          * This method is called when this package gains or loses preferred Nfc service status,
          * either the Default Wallet Role holder (see {@link
@@ -1327,7 +1327,10 @@
 
         /**
          * This method is called when an AID conflict is detected during an NFC transaction. This
-         * can happen when multiple services are registered for the same AID.
+         * can happen when multiple services are registered for the same AID. If your service is
+         * registered for this AID you may want to instruct users to bring your app to the
+         * foreground and ensure you call {@link #setPreferredService(Activity, ComponentName)}
+         * to ensure the transaction is routed to your service.
          *
          * @param aid The AID that is in conflict
          */
@@ -1377,10 +1380,10 @@
         default void onInternalErrorReported(@NfcInternalErrorType int errorType) {}
     }
 
-    private final ArrayMap<NfcEventListener, Executor> mNfcEventListeners = new ArrayMap<>();
+    private final ArrayMap<NfcEventCallback, Executor> mNfcEventCallbacks = new ArrayMap<>();
 
-    final INfcEventListener mINfcEventListener =
-            new INfcEventListener.Stub() {
+    final INfcEventCallback mINfcEventCallback =
+            new INfcEventCallback.Stub() {
                 public void onPreferredServiceChanged(ComponentNameAndUser componentNameAndUser) {
                     if (!android.nfc.Flags.nfcEventListener()) {
                         return;
@@ -1440,12 +1443,12 @@
                 }
 
                 interface ListenerCall {
-                    void invoke(NfcEventListener listener);
+                    void invoke(NfcEventCallback listener);
                 }
 
                 private void callListeners(ListenerCall listenerCall) {
-                    synchronized (mNfcEventListeners) {
-                        mNfcEventListeners.forEach(
+                    synchronized (mNfcEventCallbacks) {
+                        mNfcEventCallbacks.forEach(
                             (listener, executor) -> {
                                 executor.execute(() -> listenerCall.invoke(listener));
                             });
@@ -1460,34 +1463,34 @@
      * @param listener The listener to register
      */
     @FlaggedApi(android.nfc.Flags.FLAG_NFC_EVENT_LISTENER)
-    public void registerNfcEventListener(
-            @NonNull @CallbackExecutor Executor executor, @NonNull NfcEventListener listener) {
+    public void registerNfcEventCallback(
+            @NonNull @CallbackExecutor Executor executor, @NonNull NfcEventCallback listener) {
         if (!android.nfc.Flags.nfcEventListener()) {
             return;
         }
-        synchronized (mNfcEventListeners) {
-            mNfcEventListeners.put(listener, executor);
-            if (mNfcEventListeners.size() == 1) {
-                callService(() -> sService.registerNfcEventListener(mINfcEventListener));
+        synchronized (mNfcEventCallbacks) {
+            mNfcEventCallbacks.put(listener, executor);
+            if (mNfcEventCallbacks.size() == 1) {
+                callService(() -> sService.registerNfcEventCallback(mINfcEventCallback));
             }
         }
     }
 
     /**
      * Unregister a preferred service listener that was previously registered with {@link
-     * #registerNfcEventListener(Executor, NfcEventListener)}
+     * #registerNfcEventCallback(Executor, NfcEventCallback)}
      *
      * @param listener The previously registered listener to unregister
      */
     @FlaggedApi(android.nfc.Flags.FLAG_NFC_EVENT_LISTENER)
-    public void unregisterNfcEventListener(@NonNull NfcEventListener listener) {
+    public void unregisterNfcEventCallback(@NonNull NfcEventCallback listener) {
         if (!android.nfc.Flags.nfcEventListener()) {
             return;
         }
-        synchronized (mNfcEventListeners) {
-            mNfcEventListeners.remove(listener);
-            if (mNfcEventListeners.size() == 0) {
-                callService(() -> sService.unregisterNfcEventListener(mINfcEventListener));
+        synchronized (mNfcEventCallbacks) {
+            mNfcEventCallbacks.remove(listener);
+            if (mNfcEventCallbacks.size() == 0) {
+                callService(() -> sService.unregisterNfcEventCallback(mINfcEventCallback));
             }
         }
     }
diff --git a/nfc/java/android/nfc/cardemulation/HostApduService.java b/nfc/java/android/nfc/cardemulation/HostApduService.java
index db1f6a2..fbf2203 100644
--- a/nfc/java/android/nfc/cardemulation/HostApduService.java
+++ b/nfc/java/android/nfc/cardemulation/HostApduService.java
@@ -289,7 +289,7 @@
                         try {
                             mNfcService.send(responseMsg);
                         } catch (RemoteException e) {
-                            Log.e("TAG", "Response not sent; RemoteException calling into " +
+                            Log.e(TAG, "Response not sent; RemoteException calling into " +
                                     "NfcService.");
                         }
                     }
diff --git a/nfc/java/android/nfc/dta/OWNERS b/nfc/java/android/nfc/dta/OWNERS
deleted file mode 100644
index 35e9713..0000000
--- a/nfc/java/android/nfc/dta/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-# Bug component: 48448
-include platform/packages/apps/Nfc:/OWNERS
diff --git a/nfc/java/android/nfc/tech/OWNERS b/nfc/java/android/nfc/tech/OWNERS
deleted file mode 100644
index 35e9713..0000000
--- a/nfc/java/android/nfc/tech/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-# Bug component: 48448
-include platform/packages/apps/Nfc:/OWNERS
diff --git a/nfc/tests/Android.bp b/nfc/tests/Android.bp
index b6090e8..17fb810 100644
--- a/nfc/tests/Android.bp
+++ b/nfc/tests/Android.bp
@@ -29,7 +29,6 @@
         "androidx.test.rules",
         "androidx.test.runner",
         "androidx.test.ext.junit",
-        "framework-nfc.impl",
         "mockito-target-extended-minus-junit4",
         "frameworks-base-testutils",
         "truth",
@@ -40,16 +39,25 @@
         "testables",
     ],
     libs: [
+        "androidx.annotation_annotation",
+        "unsupportedappusage", // for android.compat.annotation.UnsupportedAppUsage
+        "framework-permission-s.stubs.module_lib",
+        "framework-permission.stubs.module_lib",
         "android.test.base.stubs.system",
         "android.test.mock.stubs.system",
         "android.test.runner.stubs.system",
+        "framework-nfc.impl",
     ],
     jni_libs: [
         // Required for ExtendedMockito
         "libdexmakerjvmtiagent",
         "libstaticjvmtiagent",
     ],
-    srcs: ["src/**/*.java"],
+    srcs: [
+        "src/**/*.java",
+        ":framework-nfc-updatable-sources",
+        ":framework-nfc-non-updatable-sources",
+    ],
     platform_apis: true,
     certificate: "platform",
     test_suites: [
diff --git a/nfc/tests/src/android/nfc/NfcAntennaInfoTest.java b/nfc/tests/src/android/nfc/NfcAntennaInfoTest.java
new file mode 100644
index 0000000..c24816d
--- /dev/null
+++ b/nfc/tests/src/android/nfc/NfcAntennaInfoTest.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.nfc;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class NfcAntennaInfoTest {
+    private NfcAntennaInfo mNfcAntennaInfo;
+
+
+    @Before
+    public void setUp() {
+        AvailableNfcAntenna availableNfcAntenna = mock(AvailableNfcAntenna.class);
+        List<AvailableNfcAntenna> antennas = new ArrayList<>();
+        antennas.add(availableNfcAntenna);
+        mNfcAntennaInfo = new NfcAntennaInfo(1, 1, false, antennas);
+    }
+
+    @After
+    public void tearDown() {
+    }
+
+    @Test
+    public void testGetDeviceHeight() {
+        int height = mNfcAntennaInfo.getDeviceHeight();
+        assertThat(height).isEqualTo(1);
+    }
+
+    @Test
+    public void testGetDeviceWidth() {
+        int width = mNfcAntennaInfo.getDeviceWidth();
+        assertThat(width).isEqualTo(1);
+    }
+
+    @Test
+    public void testIsDeviceFoldable() {
+        boolean foldable = mNfcAntennaInfo.isDeviceFoldable();
+        assertThat(foldable).isFalse();
+    }
+
+    @Test
+    public void testGetAvailableNfcAntennas() {
+        List<AvailableNfcAntenna> antennas = mNfcAntennaInfo.getAvailableNfcAntennas();
+        assertThat(antennas).isNotNull();
+        assertThat(antennas.size()).isEqualTo(1);
+    }
+
+}
diff --git a/nfc/tests/src/android/nfc/cardemulation/AidGroupTest.java b/nfc/tests/src/android/nfc/cardemulation/AidGroupTest.java
new file mode 100644
index 0000000..7e00102
--- /dev/null
+++ b/nfc/tests/src/android/nfc/cardemulation/AidGroupTest.java
@@ -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 android.nfc.cardemulation;
+
+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.isNull;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import android.os.Parcel;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class AidGroupTest {
+    private AidGroup mAidGroup;
+
+    @Before
+    public void setUp() {
+        List<String> aids = new ArrayList<>();
+        aids.add("A0000000031010");
+        aids.add("A0000000041010");
+        aids.add("A0000000034710");
+        aids.add("A000000300");
+        mAidGroup = new AidGroup(aids, "payment");
+    }
+
+    @After
+    public void tearDown() {
+    }
+
+    @Test
+    public void testGetCategory() {
+        String category = mAidGroup.getCategory();
+        assertThat(category).isNotNull();
+        assertThat(category).isEqualTo("payment");
+    }
+
+    @Test
+    public void testGetAids() {
+        List<String> aids = mAidGroup.getAids();
+        assertThat(aids).isNotNull();
+        assertThat(aids.size()).isGreaterThan(0);
+        assertThat(aids.get(0)).isEqualTo("A0000000031010");
+    }
+
+    @Test
+    public void testWriteAsXml() throws IOException {
+        XmlSerializer out = mock(XmlSerializer.class);
+        mAidGroup.writeAsXml(out);
+        verify(out, atLeastOnce()).startTag(isNull(), anyString());
+        verify(out, atLeastOnce()).attribute(isNull(), anyString(), anyString());
+        verify(out, atLeastOnce()).endTag(isNull(), anyString());
+    }
+
+    @Test
+    public void testRightToParcel() {
+        Parcel parcel = mock(Parcel.class);
+        mAidGroup.writeToParcel(parcel, 0);
+        verify(parcel).writeString8(anyString());
+        verify(parcel).writeInt(anyInt());
+        verify(parcel).writeStringList(any());
+    }
+}
diff --git a/packages/CompanionDeviceManager/res/values/strings.xml b/packages/CompanionDeviceManager/res/values/strings.xml
index b266912..2a6d68d 100644
--- a/packages/CompanionDeviceManager/res/values/strings.xml
+++ b/packages/CompanionDeviceManager/res/values/strings.xml
@@ -88,6 +88,17 @@
     <!-- Description of the helper dialog for NEARBY_DEVICE_STREAMING profile. [CHAR LIMIT=NONE] -->
     <string name="helper_summary_nearby_device_streaming"><xliff:g id="app_name" example="Exo">%1$s</xliff:g> is requesting permission on behalf of <xliff:g id="device_name" example="Chromebook">%2$s</xliff:g> to stream apps from your <xliff:g id="device_type" example="phone">%3$s</xliff:g></string>
 
+    <!-- ================= DEVICE_PROFILE_SENSOR_DEVICE_STREAMING ================= -->
+
+    <!-- Confirmation for associating an application with a companion device of SENSOR_DEVICE_STREAMING profile (type) [CHAR LIMIT=NONE] -->
+    <string name="title_sensor_device_streaming">Allow &lt;strong&gt;<xliff:g id="app_name" example="Exo">%1$s</xliff:g>&lt;/strong&gt; to stream audio and system features between your <xliff:g id="device_type" example="phone">%2$s</xliff:g> and &lt;strong&gt;<xliff:g id="device_name" example="Chromebook">%3$s</xliff:g>&lt;/strong&gt;?</string>
+
+    <!-- Summary for associating an application with a companion device of SENSOR_DEVICE_STREAMING profile [CHAR LIMIT=NONE] -->
+    <string name="summary_sensor_device_streaming"><xliff:g id="app_name" example="Exo">%1$s</xliff:g> will have access to anything that’s played on your <xliff:g id="device_name" example="Chromebook">%3$s</xliff:g>.&lt;br/>&lt;br/><xliff:g id="app_name" example="Exo">%1$s</xliff:g> will be able to stream audio to <xliff:g id="device_name" example="Chromebook">%3$s</xliff:g> until you remove access to this permission.</string>
+
+    <!-- Description of the helper dialog for SENSOR_DEVICE_STREAMING profile. [CHAR LIMIT=NONE] -->
+    <string name="helper_summary_sensor_device_streaming"><xliff:g id="app_name" example="Exo">%1$s</xliff:g> is requesting permission on behalf of <xliff:g id="device_name" example="Chromebook">%2$s</xliff:g> to stream audio and system features between your devices.</string>
+
     <!-- ================= null profile ================= -->
 
     <!-- A noun for a companion device with unspecified profile (type) [CHAR LIMIT=30] -->
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceResources.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceResources.java
index 37b1f29..fd77164 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceResources.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceResources.java
@@ -21,6 +21,7 @@
 import static android.companion.AssociationRequest.DEVICE_PROFILE_COMPUTER;
 import static android.companion.AssociationRequest.DEVICE_PROFILE_GLASSES;
 import static android.companion.AssociationRequest.DEVICE_PROFILE_NEARBY_DEVICE_STREAMING;
+import static android.companion.AssociationRequest.DEVICE_PROFILE_SENSOR_DEVICE_STREAMING;
 import static android.companion.AssociationRequest.DEVICE_PROFILE_WATCH;
 import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE;
 
@@ -116,6 +117,7 @@
         map.put(DEVICE_PROFILE_AUTOMOTIVE_PROJECTION, R.string.title_automotive_projection);
         map.put(DEVICE_PROFILE_COMPUTER, R.string.title_computer);
         map.put(DEVICE_PROFILE_NEARBY_DEVICE_STREAMING, R.string.title_nearby_device_streaming);
+        map.put(DEVICE_PROFILE_SENSOR_DEVICE_STREAMING, R.string.title_sensor_device_streaming);
         map.put(DEVICE_PROFILE_WATCH, R.string.confirmation_title);
         map.put(DEVICE_PROFILE_GLASSES, R.string.confirmation_title_glasses);
         map.put(null, R.string.confirmation_title);
@@ -130,6 +132,7 @@
         map.put(DEVICE_PROFILE_GLASSES, R.string.summary_glasses);
         map.put(DEVICE_PROFILE_APP_STREAMING, R.string.summary_app_streaming);
         map.put(DEVICE_PROFILE_NEARBY_DEVICE_STREAMING, R.string.summary_nearby_device_streaming);
+        map.put(DEVICE_PROFILE_SENSOR_DEVICE_STREAMING, R.string.summary_sensor_device_streaming);
         map.put(null, R.string.summary_generic);
 
         PROFILE_SUMMARIES = unmodifiableMap(map);
@@ -141,6 +144,8 @@
         map.put(DEVICE_PROFILE_APP_STREAMING, R.string.helper_summary_app_streaming);
         map.put(DEVICE_PROFILE_NEARBY_DEVICE_STREAMING,
                 R.string.helper_summary_nearby_device_streaming);
+        map.put(DEVICE_PROFILE_SENSOR_DEVICE_STREAMING,
+                R.string.helper_summary_sensor_device_streaming);
         map.put(DEVICE_PROFILE_COMPUTER, R.string.helper_summary_computer);
 
         PROFILE_HELPER_SUMMARIES = unmodifiableMap(map);
@@ -204,6 +209,7 @@
         set.add(DEVICE_PROFILE_COMPUTER);
         set.add(DEVICE_PROFILE_AUTOMOTIVE_PROJECTION);
         set.add(DEVICE_PROFILE_NEARBY_DEVICE_STREAMING);
+        set.add(DEVICE_PROFILE_SENSOR_DEVICE_STREAMING);
         set.add(null);
 
         SUPPORTED_SELF_MANAGED_PROFILES = unmodifiableSet(set);
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 8b8ab58..31e1eb3 100644
--- a/packages/CrashRecovery/services/module/java/com/android/server/PackageWatchdog.java
+++ b/packages/CrashRecovery/services/module/java/com/android/server/PackageWatchdog.java
@@ -767,6 +767,70 @@
     }
 
     /**
+     * Indicates that the result of a mitigation executed during
+     * {@link PackageHealthObserver#onExecuteHealthCheckMitigation} or
+     * {@link PackageHealthObserver#onExecuteBootLoopMitigation} is unknown.
+     */
+    public static final int MITIGATION_RESULT_UNKNOWN =
+            ObserverMitigationResult.MITIGATION_RESULT_UNKNOWN;
+
+    /**
+     * Indicates that a mitigation was successfully triggered or executed during
+     * {@link PackageHealthObserver#onExecuteHealthCheckMitigation} or
+     * {@link PackageHealthObserver#onExecuteBootLoopMitigation}.
+     */
+    public static final int MITIGATION_RESULT_SUCCESS =
+            ObserverMitigationResult.MITIGATION_RESULT_SUCCESS;
+
+    /**
+     * Indicates that a mitigation executed during
+     * {@link PackageHealthObserver#onExecuteHealthCheckMitigation} or
+     * {@link PackageHealthObserver#onExecuteBootLoopMitigation} was skipped.
+     */
+    public static final int MITIGATION_RESULT_SKIPPED =
+            ObserverMitigationResult.MITIGATION_RESULT_SKIPPED;
+
+    /**
+     * Indicates that a mitigation executed during
+     * {@link PackageHealthObserver#onExecuteHealthCheckMitigation} or
+     * {@link PackageHealthObserver#onExecuteBootLoopMitigation} failed,
+     * but the failure is potentially retryable.
+     */
+    public static final int MITIGATION_RESULT_FAILURE_RETRYABLE =
+            ObserverMitigationResult.MITIGATION_RESULT_FAILURE_RETRYABLE;
+
+    /**
+     * Indicates that a mitigation executed during
+     * {@link PackageHealthObserver#onExecuteHealthCheckMitigation} or
+     * {@link PackageHealthObserver#onExecuteBootLoopMitigation} failed,
+     * and the failure is not retryable.
+     */
+    public static final int MITIGATION_RESULT_FAILURE_NON_RETRYABLE =
+            ObserverMitigationResult.MITIGATION_RESULT_FAILURE_NON_RETRYABLE;
+
+    /**
+     * Possible return values of the for mitigations executed during
+     * {@link PackageHealthObserver#onExecuteHealthCheckMitigation} and
+     * {@link PackageHealthObserver#onExecuteBootLoopMitigation}.
+     * @hide
+     */
+    @Retention(SOURCE)
+    @IntDef(prefix = "MITIGATION_RESULT_", value = {
+            ObserverMitigationResult.MITIGATION_RESULT_UNKNOWN,
+            ObserverMitigationResult.MITIGATION_RESULT_SUCCESS,
+            ObserverMitigationResult.MITIGATION_RESULT_SKIPPED,
+            ObserverMitigationResult.MITIGATION_RESULT_FAILURE_RETRYABLE,
+            ObserverMitigationResult.MITIGATION_RESULT_FAILURE_NON_RETRYABLE,
+            })
+    public @interface ObserverMitigationResult {
+        int MITIGATION_RESULT_UNKNOWN = 0;
+        int MITIGATION_RESULT_SUCCESS = 1;
+        int MITIGATION_RESULT_SKIPPED = 2;
+        int MITIGATION_RESULT_FAILURE_RETRYABLE = 3;
+        int MITIGATION_RESULT_FAILURE_NON_RETRYABLE = 4;
+    }
+
+    /**
      * The minimum value that can be returned by any observer.
      * It represents that no mitigations were available.
      */
@@ -852,13 +916,20 @@
          * health check.
          *
          * @param versionedPackage the package that is failing. This may be null if a native
-         *                          service is crashing.
-         * @param failureReason   the type of failure that is occurring.
+         *                         service is crashing.
+         * @param failureReason    the type of failure that is occurring.
          * @param mitigationCount the number of times mitigation has been called for this package
-         *                        (including this time).
-         * @return {@code true} if action was executed successfully, {@code false} otherwise
+         *                         (including this time).
+         * @return {@link #MITIGATION_RESULT_SUCCESS} if the mitigation was successful,
+         *         {@link #MITIGATION_RESULT_FAILURE_RETRYABLE} if the mitigation failed but can be
+         *         retried,
+         *         {@link #MITIGATION_RESULT_FAILURE_NON_RETRYABLE} if the mitigation failed and
+         *         cannot be retried,
+         *         {@link #MITIGATION_RESULT_UNKNOWN} if the result of the mitigation is unknown,
+         *         or {@link #MITIGATION_RESULT_SKIPPED} if the mitigation was skipped.
          */
-        boolean onExecuteHealthCheckMitigation(@Nullable VersionedPackage versionedPackage,
+        @ObserverMitigationResult int onExecuteHealthCheckMitigation(
+                @Nullable VersionedPackage versionedPackage,
                 @FailureReasons int failureReason, int mitigationCount);
 
 
@@ -885,10 +956,16 @@
          * @param mitigationCount the number of times mitigation has been attempted for this
          *                        boot loop (including this time).
          *
-         * @return {@code true} if action was executed successfully, {@code false} otherwise
+         * @return {@link #MITIGATION_RESULT_SUCCESS} if the mitigation was successful,
+         *         {@link #MITIGATION_RESULT_FAILURE_RETRYABLE} if the mitigation failed but can be
+         *         retried,
+         *         {@link #MITIGATION_RESULT_FAILURE_NON_RETRYABLE} if the mitigation failed and
+         *         cannot be retried,
+         *         {@link #MITIGATION_RESULT_UNKNOWN} if the result of the mitigation is unknown,
+         *         or {@link #MITIGATION_RESULT_SKIPPED} if the mitigation was skipped.
          */
-        default boolean onExecuteBootLoopMitigation(int mitigationCount) {
-            return false;
+        default @ObserverMitigationResult int onExecuteBootLoopMitigation(int mitigationCount) {
+            return ObserverMitigationResult.MITIGATION_RESULT_SKIPPED;
         }
 
         // TODO(b/120598832): Ensure uniqueness?
diff --git a/packages/CrashRecovery/services/module/java/com/android/server/RescueParty.java b/packages/CrashRecovery/services/module/java/com/android/server/RescueParty.java
index bad6ab7..bb9e962 100644
--- a/packages/CrashRecovery/services/module/java/com/android/server/RescueParty.java
+++ b/packages/CrashRecovery/services/module/java/com/android/server/RescueParty.java
@@ -16,6 +16,8 @@
 
 package com.android.server;
 
+import static com.android.server.PackageWatchdog.MITIGATION_RESULT_SKIPPED;
+import static com.android.server.PackageWatchdog.MITIGATION_RESULT_SUCCESS;
 import static com.android.server.crashrecovery.CrashRecoveryUtils.logCrashRecoveryEvent;
 
 import android.annotation.IntDef;
@@ -728,10 +730,10 @@
         }
 
         @Override
-        public boolean onExecuteHealthCheckMitigation(@Nullable VersionedPackage failedPackage,
+        public int onExecuteHealthCheckMitigation(@Nullable VersionedPackage failedPackage,
                 @FailureReasons int failureReason, int mitigationCount) {
             if (isDisabled()) {
-                return false;
+                return MITIGATION_RESULT_SKIPPED;
             }
             Slog.i(TAG, "Executing remediation."
                     + " failedPackage: "
@@ -753,9 +755,9 @@
                 }
                 executeRescueLevel(mContext,
                         failedPackage == null ? null : failedPackage.getPackageName(), level);
-                return true;
+                return MITIGATION_RESULT_SUCCESS;
             } else {
-                return false;
+                return MITIGATION_RESULT_SKIPPED;
             }
         }
 
@@ -796,9 +798,9 @@
         }
 
         @Override
-        public boolean onExecuteBootLoopMitigation(int mitigationCount) {
+        public int onExecuteBootLoopMitigation(int mitigationCount) {
             if (isDisabled()) {
-                return false;
+                return MITIGATION_RESULT_SKIPPED;
             }
             boolean mayPerformReboot = !shouldThrottleReboot();
             final int level;
@@ -813,7 +815,7 @@
                 level = getRescueLevel(mitigationCount, mayPerformReboot);
             }
             executeRescueLevel(mContext, /*failedPackage=*/ null, level);
-            return true;
+            return MITIGATION_RESULT_SUCCESS;
         }
 
         @Override
diff --git a/packages/CrashRecovery/services/module/java/com/android/server/rollback/RollbackPackageHealthObserver.java b/packages/CrashRecovery/services/module/java/com/android/server/rollback/RollbackPackageHealthObserver.java
index c80a1a4..c75f3aa 100644
--- a/packages/CrashRecovery/services/module/java/com/android/server/rollback/RollbackPackageHealthObserver.java
+++ b/packages/CrashRecovery/services/module/java/com/android/server/rollback/RollbackPackageHealthObserver.java
@@ -16,6 +16,8 @@
 
 package com.android.server.rollback;
 
+import static com.android.server.PackageWatchdog.MITIGATION_RESULT_SKIPPED;
+import static com.android.server.PackageWatchdog.MITIGATION_RESULT_SUCCESS;
 import static com.android.server.crashrecovery.CrashRecoveryUtils.logCrashRecoveryEvent;
 
 import android.annotation.AnyThread;
@@ -172,7 +174,7 @@
     }
 
     @Override
-    public boolean onExecuteHealthCheckMitigation(@Nullable VersionedPackage failedPackage,
+    public int onExecuteHealthCheckMitigation(@Nullable VersionedPackage failedPackage,
             @FailureReasons int rollbackReason, int mitigationCount) {
         Slog.i(TAG, "Executing remediation."
                 + " failedPackage: "
@@ -183,7 +185,7 @@
             List<RollbackInfo> availableRollbacks = getAvailableRollbacks();
             if (rollbackReason == PackageWatchdog.FAILURE_REASON_NATIVE_CRASH) {
                 mHandler.post(() -> rollbackAllLowImpact(availableRollbacks, rollbackReason));
-                return true;
+                return MITIGATION_RESULT_SUCCESS;
             }
 
             List<RollbackInfo> lowImpactRollbacks = getRollbacksAvailableForImpactLevel(
@@ -198,7 +200,7 @@
         } else {
             if (rollbackReason == PackageWatchdog.FAILURE_REASON_NATIVE_CRASH) {
                 mHandler.post(() -> rollbackAll(rollbackReason));
-                return true;
+                return MITIGATION_RESULT_SUCCESS;
             }
 
             RollbackInfo rollback = getAvailableRollback(failedPackage);
@@ -210,7 +212,7 @@
         }
 
         // Assume rollbacks executed successfully
-        return true;
+        return MITIGATION_RESULT_SUCCESS;
     }
 
     @Override
@@ -226,15 +228,15 @@
     }
 
     @Override
-    public boolean onExecuteBootLoopMitigation(int mitigationCount) {
+    public int onExecuteBootLoopMitigation(int mitigationCount) {
         if (Flags.recoverabilityDetection()) {
             List<RollbackInfo> availableRollbacks = getAvailableRollbacks();
 
             triggerLeastImpactLevelRollback(availableRollbacks,
                     PackageWatchdog.FAILURE_REASON_BOOT_LOOP);
-            return true;
+            return MITIGATION_RESULT_SUCCESS;
         }
-        return false;
+        return MITIGATION_RESULT_SKIPPED;
     }
 
     @Override
diff --git a/packages/CrashRecovery/services/platform/java/com/android/server/PackageWatchdog.java b/packages/CrashRecovery/services/platform/java/com/android/server/PackageWatchdog.java
index 4fea937..ffae517 100644
--- a/packages/CrashRecovery/services/platform/java/com/android/server/PackageWatchdog.java
+++ b/packages/CrashRecovery/services/platform/java/com/android/server/PackageWatchdog.java
@@ -752,6 +752,70 @@
         return mPackagesExemptFromImpactLevelThreshold;
     }
 
+    /**
+     * Indicates that the result of a mitigation executed during
+     * {@link PackageHealthObserver#onExecuteHealthCheckMitigation} or
+     * {@link PackageHealthObserver#onExecuteBootLoopMitigation} is unknown.
+     */
+    public static final int MITIGATION_RESULT_UNKNOWN =
+            ObserverMitigationResult.MITIGATION_RESULT_UNKNOWN;
+
+    /**
+     * Indicates that a mitigation was successfully triggered or executed during
+     * {@link PackageHealthObserver#onExecuteHealthCheckMitigation} or
+     * {@link PackageHealthObserver#onExecuteBootLoopMitigation}.
+     */
+    public static final int MITIGATION_RESULT_SUCCESS =
+            ObserverMitigationResult.MITIGATION_RESULT_SUCCESS;
+
+    /**
+     * Indicates that a mitigation executed during
+     * {@link PackageHealthObserver#onExecuteHealthCheckMitigation} or
+     * {@link PackageHealthObserver#onExecuteBootLoopMitigation} was skipped.
+     */
+    public static final int MITIGATION_RESULT_SKIPPED =
+            ObserverMitigationResult.MITIGATION_RESULT_SKIPPED;
+
+    /**
+     * Indicates that a mitigation executed during
+     * {@link PackageHealthObserver#onExecuteHealthCheckMitigation} or
+     * {@link PackageHealthObserver#onExecuteBootLoopMitigation} failed,
+     * but the failure is potentially retryable.
+     */
+    public static final int MITIGATION_RESULT_FAILURE_RETRYABLE =
+            ObserverMitigationResult.MITIGATION_RESULT_FAILURE_RETRYABLE;
+
+    /**
+     * Indicates that a mitigation executed during
+     * {@link PackageHealthObserver#onExecuteHealthCheckMitigation} or
+     * {@link PackageHealthObserver#onExecuteBootLoopMitigation} failed,
+     * and the failure is not retryable.
+     */
+    public static final int MITIGATION_RESULT_FAILURE_NON_RETRYABLE =
+            ObserverMitigationResult.MITIGATION_RESULT_FAILURE_NON_RETRYABLE;
+
+    /**
+     * Possible return values of the for mitigations executed during
+     * {@link PackageHealthObserver#onExecuteHealthCheckMitigation} and
+     * {@link PackageHealthObserver#onExecuteBootLoopMitigation}.
+     * @hide
+     */
+    @Retention(SOURCE)
+    @IntDef(prefix = "MITIGATION_RESULT_", value = {
+            ObserverMitigationResult.MITIGATION_RESULT_UNKNOWN,
+            ObserverMitigationResult.MITIGATION_RESULT_SUCCESS,
+            ObserverMitigationResult.MITIGATION_RESULT_SKIPPED,
+            ObserverMitigationResult.MITIGATION_RESULT_FAILURE_RETRYABLE,
+            ObserverMitigationResult.MITIGATION_RESULT_FAILURE_NON_RETRYABLE,
+    })
+    public @interface ObserverMitigationResult {
+        int MITIGATION_RESULT_UNKNOWN = 0;
+        int MITIGATION_RESULT_SUCCESS = 1;
+        int MITIGATION_RESULT_SKIPPED = 2;
+        int MITIGATION_RESULT_FAILURE_RETRYABLE = 3;
+        int MITIGATION_RESULT_FAILURE_NON_RETRYABLE = 4;
+    }
+
     /** Possible severity values of the user impact of a
      * {@link PackageHealthObserver#onExecuteHealthCheckMitigation}.
      * @hide
@@ -809,16 +873,25 @@
                 int mitigationCount);
 
         /**
-         * Executes mitigation for {@link #onHealthCheckFailed}.
+         * This would be called after {@link #onHealthCheckFailed}.
+         * This is called only if current observer returned least impact mitigation for failed
+         * health check.
          *
          * @param versionedPackage the package that is failing. This may be null if a native
-         *                          service is crashing.
-         * @param failureReason   the type of failure that is occurring.
+         *                         service is crashing.
+         * @param failureReason    the type of failure that is occurring.
          * @param mitigationCount the number of times mitigation has been called for this package
-         *                        (including this time).
-         * @return {@code true} if action was executed successfully, {@code false} otherwise
+         *                         (including this time).
+         * @return {@link #MITIGATION_RESULT_SUCCESS} if the mitigation was successful,
+         *         {@link #MITIGATION_RESULT_FAILURE_RETRYABLE} if the mitigation failed but can be
+         *         retried,
+         *         {@link #MITIGATION_RESULT_FAILURE_NON_RETRYABLE} if the mitigation failed and
+         *         cannot be retried,
+         *         {@link #MITIGATION_RESULT_UNKNOWN} if the result of the mitigation is unknown,
+         *         or {@link #MITIGATION_RESULT_SKIPPED} if the mitigation was skipped.
          */
-        boolean onExecuteHealthCheckMitigation(@Nullable VersionedPackage versionedPackage,
+        @ObserverMitigationResult int onExecuteHealthCheckMitigation(
+                @Nullable VersionedPackage versionedPackage,
                 @FailureReasons int failureReason, int mitigationCount);
 
 
@@ -834,12 +907,23 @@
         }
 
         /**
-         * Executes mitigation for {@link #onBootLoop}
+         * This would be called after {@link #onBootLoop}.
+         * This is called only if current observer returned least impact mitigation for fixing
+         * boot loop.
+         *
          * @param mitigationCount the number of times mitigation has been attempted for this
          *                        boot loop (including this time).
+         *
+         * @return {@link #MITIGATION_RESULT_SUCCESS} if the mitigation was successful,
+         *         {@link #MITIGATION_RESULT_FAILURE_RETRYABLE} if the mitigation failed but can be
+         *         retried,
+         *         {@link #MITIGATION_RESULT_FAILURE_NON_RETRYABLE} if the mitigation failed and
+         *         cannot be retried,
+         *         {@link #MITIGATION_RESULT_UNKNOWN} if the result of the mitigation is unknown,
+         *         or {@link #MITIGATION_RESULT_SKIPPED} if the mitigation was skipped.
          */
-        default boolean onExecuteBootLoopMitigation(int mitigationCount) {
-            return false;
+        default @ObserverMitigationResult int onExecuteBootLoopMitigation(int mitigationCount) {
+            return ObserverMitigationResult.MITIGATION_RESULT_SKIPPED;
         }
 
         // TODO(b/120598832): Ensure uniqueness?
diff --git a/packages/CrashRecovery/services/platform/java/com/android/server/RescueParty.java b/packages/CrashRecovery/services/platform/java/com/android/server/RescueParty.java
index 2bb72fb..c6452d3 100644
--- a/packages/CrashRecovery/services/platform/java/com/android/server/RescueParty.java
+++ b/packages/CrashRecovery/services/platform/java/com/android/server/RescueParty.java
@@ -18,6 +18,8 @@
 
 import static android.provider.DeviceConfig.Properties;
 
+import static com.android.server.PackageWatchdog.MITIGATION_RESULT_SKIPPED;
+import static com.android.server.PackageWatchdog.MITIGATION_RESULT_SUCCESS;
 import static com.android.server.crashrecovery.CrashRecoveryUtils.logCrashRecoveryEvent;
 
 import android.annotation.IntDef;
@@ -859,10 +861,10 @@
         }
 
         @Override
-        public boolean onExecuteHealthCheckMitigation(@Nullable VersionedPackage failedPackage,
+        public int onExecuteHealthCheckMitigation(@Nullable VersionedPackage failedPackage,
                 @FailureReasons int failureReason, int mitigationCount) {
             if (isDisabled()) {
-                return false;
+                return MITIGATION_RESULT_SKIPPED;
             }
             Slog.i(TAG, "Executing remediation."
                     + " failedPackage: "
@@ -884,9 +886,9 @@
                 }
                 executeRescueLevel(mContext,
                         failedPackage == null ? null : failedPackage.getPackageName(), level);
-                return true;
+                return MITIGATION_RESULT_SUCCESS;
             } else {
-                return false;
+                return MITIGATION_RESULT_SKIPPED;
             }
         }
 
@@ -927,9 +929,9 @@
         }
 
         @Override
-        public boolean onExecuteBootLoopMitigation(int mitigationCount) {
+        public int onExecuteBootLoopMitigation(int mitigationCount) {
             if (isDisabled()) {
-                return false;
+                return MITIGATION_RESULT_SKIPPED;
             }
             boolean mayPerformReboot = !shouldThrottleReboot();
             final int level;
@@ -944,7 +946,7 @@
                 level = getRescueLevel(mitigationCount, mayPerformReboot);
             }
             executeRescueLevel(mContext, /*failedPackage=*/ null, level);
-            return true;
+            return MITIGATION_RESULT_SUCCESS;
         }
 
         @Override
diff --git a/packages/CrashRecovery/services/platform/java/com/android/server/rollback/RollbackPackageHealthObserver.java b/packages/CrashRecovery/services/platform/java/com/android/server/rollback/RollbackPackageHealthObserver.java
index 0692cdb..0411537 100644
--- a/packages/CrashRecovery/services/platform/java/com/android/server/rollback/RollbackPackageHealthObserver.java
+++ b/packages/CrashRecovery/services/platform/java/com/android/server/rollback/RollbackPackageHealthObserver.java
@@ -18,6 +18,8 @@
 
 import static android.content.pm.Flags.provideInfoOfApkInApex;
 
+import static com.android.server.PackageWatchdog.MITIGATION_RESULT_SKIPPED;
+import static com.android.server.PackageWatchdog.MITIGATION_RESULT_SUCCESS;
 import static com.android.server.crashrecovery.CrashRecoveryUtils.logCrashRecoveryEvent;
 
 import android.annotation.AnyThread;
@@ -175,7 +177,7 @@
     }
 
     @Override
-    public boolean onExecuteHealthCheckMitigation(@Nullable VersionedPackage failedPackage,
+    public int onExecuteHealthCheckMitigation(@Nullable VersionedPackage failedPackage,
             @FailureReasons int rollbackReason, int mitigationCount) {
         Slog.i(TAG, "Executing remediation."
                 + " failedPackage: "
@@ -186,7 +188,7 @@
             List<RollbackInfo> availableRollbacks = getAvailableRollbacks();
             if (rollbackReason == PackageWatchdog.FAILURE_REASON_NATIVE_CRASH) {
                 mHandler.post(() -> rollbackAllLowImpact(availableRollbacks, rollbackReason));
-                return true;
+                return MITIGATION_RESULT_SUCCESS;
             }
 
             List<RollbackInfo> lowImpactRollbacks = getRollbacksAvailableForImpactLevel(
@@ -201,7 +203,7 @@
         } else {
             if (rollbackReason == PackageWatchdog.FAILURE_REASON_NATIVE_CRASH) {
                 mHandler.post(() -> rollbackAll(rollbackReason));
-                return true;
+                return MITIGATION_RESULT_SUCCESS;
             }
 
             RollbackInfo rollback = getAvailableRollback(failedPackage);
@@ -213,7 +215,7 @@
         }
 
         // Assume rollbacks executed successfully
-        return true;
+        return MITIGATION_RESULT_SUCCESS;
     }
 
     @Override
@@ -229,15 +231,15 @@
     }
 
     @Override
-    public boolean onExecuteBootLoopMitigation(int mitigationCount) {
+    public int onExecuteBootLoopMitigation(int mitigationCount) {
         if (Flags.recoverabilityDetection()) {
             List<RollbackInfo> availableRollbacks = getAvailableRollbacks();
 
             triggerLeastImpactLevelRollback(availableRollbacks,
                     PackageWatchdog.FAILURE_REASON_BOOT_LOOP);
-            return true;
+            return MITIGATION_RESULT_SUCCESS;
         }
-        return false;
+        return MITIGATION_RESULT_SKIPPED;
     }
 
     @Override
diff --git a/packages/CredentialManager/wear/res/values-ta/strings.xml b/packages/CredentialManager/wear/res/values-ta/strings.xml
index 9f88c81..498d83b 100644
--- a/packages/CredentialManager/wear/res/values-ta/strings.xml
+++ b/packages/CredentialManager/wear/res/values-ta/strings.xml
@@ -19,7 +19,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="7384524142163511792">"அனுமதிச் சான்று நிர்வாகி"</string>
     <string name="use_passkey_title" msgid="716598039340757817">"கடவுச்சாவியைப் பயன்படுத்தவா?"</string>
-    <string name="use_password_title" msgid="4655101984031246476">"கடவுச்சொல்லைப் பயன்படுத்தவா?"</string>
+    <string name="use_password_title" msgid="4655101984031246476">"கடவுச்சொல் பயன்படுத்தவா?"</string>
     <string name="dialog_dismiss_button" msgid="989567669882005067">"மூடு"</string>
     <string name="dialog_continue_button" msgid="8630290044077052145">"தொடர்க"</string>
     <string name="dialog_sign_in_options_button" msgid="448002958902615054">"உள்நுழைவு விருப்பங்கள்"</string>
diff --git a/packages/CredentialManager/wear/res/values-te/strings.xml b/packages/CredentialManager/wear/res/values-te/strings.xml
index 086b109..ff85a89 100644
--- a/packages/CredentialManager/wear/res/values-te/strings.xml
+++ b/packages/CredentialManager/wear/res/values-te/strings.xml
@@ -23,7 +23,7 @@
     <string name="dialog_dismiss_button" msgid="989567669882005067">"విస్మరించండి"</string>
     <string name="dialog_continue_button" msgid="8630290044077052145">"కొనసాగించండి"</string>
     <string name="dialog_sign_in_options_button" msgid="448002958902615054">"సైన్ ఇన్ ఆప్షన్‌లు"</string>
-    <string name="sign_in_options_title" msgid="6720572645638986680">"సైన్ ఇన్ ఆప్షన్‌లు"</string>
+    <string name="sign_in_options_title" msgid="6720572645638986680">"సైన్-ఇన్ ఆప్షన్‌లు"</string>
     <string name="provider_list_title" msgid="6803918216129492212">"సైన్‌ ఇన్‌లను మేనేజ్ చేయండి"</string>
     <string name="choose_sign_in_title" msgid="3616025924746872202">"సైన్ ఇన్‌ను ఎంచుకోండి"</string>
     <string name="choose_passkey_title" msgid="8459270617632817465">"పాస్-కీని ఎంచుకోండి"</string>
diff --git a/packages/NeuralNetworks/framework/Android.bp b/packages/NeuralNetworks/framework/Android.bp
index 6f45daa..af071ba 100644
--- a/packages/NeuralNetworks/framework/Android.bp
+++ b/packages/NeuralNetworks/framework/Android.bp
@@ -19,10 +19,21 @@
 filegroup {
     name: "framework-ondeviceintelligence-sources",
     srcs: [
-        "java/**/*.aidl",
-        "java/**/*.java",
+        "module/java/**/*.aidl",
+        "module/java/**/*.java",
     ],
-    path: "java",
+    visibility: [
+        "//frameworks/base:__subpackages__",
+        "//packages/modules/NeuralNetworks:__subpackages__",
+    ],
+}
+
+filegroup {
+    name: "framework-ondeviceintelligence-sources-platform",
+    srcs: [
+        "platform/java/**/*.aidl",
+        "platform/java/**/*.java",
+    ],
     visibility: [
         "//frameworks/base:__subpackages__",
         "//packages/modules/NeuralNetworks:__subpackages__",
diff --git a/packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/DownloadCallback.java b/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/DownloadCallback.java
similarity index 100%
rename from packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/DownloadCallback.java
rename to packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/DownloadCallback.java
diff --git a/packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/Feature.aidl b/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/Feature.aidl
similarity index 100%
rename from packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/Feature.aidl
rename to packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/Feature.aidl
diff --git a/packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/Feature.java b/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/Feature.java
similarity index 100%
rename from packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/Feature.java
rename to packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/Feature.java
diff --git a/packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/FeatureDetails.aidl b/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/FeatureDetails.aidl
similarity index 100%
rename from packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/FeatureDetails.aidl
rename to packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/FeatureDetails.aidl
diff --git a/packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/FeatureDetails.java b/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/FeatureDetails.java
similarity index 100%
rename from packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/FeatureDetails.java
rename to packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/FeatureDetails.java
diff --git a/packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/ICancellationSignal.aidl b/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/ICancellationSignal.aidl
similarity index 100%
rename from packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/ICancellationSignal.aidl
rename to packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/ICancellationSignal.aidl
diff --git a/packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/IDownloadCallback.aidl b/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/IDownloadCallback.aidl
similarity index 100%
rename from packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/IDownloadCallback.aidl
rename to packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/IDownloadCallback.aidl
diff --git a/packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/IFeatureCallback.aidl b/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/IFeatureCallback.aidl
similarity index 100%
rename from packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/IFeatureCallback.aidl
rename to packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/IFeatureCallback.aidl
diff --git a/packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/IFeatureDetailsCallback.aidl b/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/IFeatureDetailsCallback.aidl
similarity index 100%
rename from packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/IFeatureDetailsCallback.aidl
rename to packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/IFeatureDetailsCallback.aidl
diff --git a/packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/IListFeaturesCallback.aidl b/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/IListFeaturesCallback.aidl
similarity index 100%
rename from packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/IListFeaturesCallback.aidl
rename to packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/IListFeaturesCallback.aidl
diff --git a/packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/IOnDeviceIntelligenceManager.aidl b/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/IOnDeviceIntelligenceManager.aidl
similarity index 100%
rename from packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/IOnDeviceIntelligenceManager.aidl
rename to packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/IOnDeviceIntelligenceManager.aidl
diff --git a/packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/IProcessingSignal.aidl b/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/IProcessingSignal.aidl
similarity index 100%
rename from packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/IProcessingSignal.aidl
rename to packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/IProcessingSignal.aidl
diff --git a/packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/IRemoteCallback.aidl b/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/IRemoteCallback.aidl
similarity index 100%
rename from packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/IRemoteCallback.aidl
rename to packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/IRemoteCallback.aidl
diff --git a/packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/IResponseCallback.aidl b/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/IResponseCallback.aidl
similarity index 100%
rename from packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/IResponseCallback.aidl
rename to packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/IResponseCallback.aidl
diff --git a/packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/IStreamingResponseCallback.aidl b/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/IStreamingResponseCallback.aidl
similarity index 100%
rename from packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/IStreamingResponseCallback.aidl
rename to packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/IStreamingResponseCallback.aidl
diff --git a/packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/ITokenInfoCallback.aidl b/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/ITokenInfoCallback.aidl
similarity index 100%
rename from packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/ITokenInfoCallback.aidl
rename to packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/ITokenInfoCallback.aidl
diff --git a/packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/InferenceInfo.aidl b/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/InferenceInfo.aidl
similarity index 100%
rename from packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/InferenceInfo.aidl
rename to packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/InferenceInfo.aidl
diff --git a/packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/InferenceInfo.java b/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/InferenceInfo.java
similarity index 100%
rename from packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/InferenceInfo.java
rename to packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/InferenceInfo.java
diff --git a/packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/OnDeviceIntelligenceException.java b/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/OnDeviceIntelligenceException.java
similarity index 100%
rename from packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/OnDeviceIntelligenceException.java
rename to packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/OnDeviceIntelligenceException.java
diff --git a/packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/OnDeviceIntelligenceFrameworkInitializer.java b/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/OnDeviceIntelligenceFrameworkInitializer.java
similarity index 100%
rename from packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/OnDeviceIntelligenceFrameworkInitializer.java
rename to packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/OnDeviceIntelligenceFrameworkInitializer.java
diff --git a/packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/OnDeviceIntelligenceManager.java b/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/OnDeviceIntelligenceManager.java
similarity index 100%
rename from packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/OnDeviceIntelligenceManager.java
rename to packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/OnDeviceIntelligenceManager.java
diff --git a/packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/ProcessingCallback.java b/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/ProcessingCallback.java
similarity index 100%
rename from packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/ProcessingCallback.java
rename to packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/ProcessingCallback.java
diff --git a/packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/ProcessingSignal.java b/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/ProcessingSignal.java
similarity index 100%
rename from packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/ProcessingSignal.java
rename to packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/ProcessingSignal.java
diff --git a/packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/StreamingProcessingCallback.java b/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/StreamingProcessingCallback.java
similarity index 100%
rename from packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/StreamingProcessingCallback.java
rename to packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/StreamingProcessingCallback.java
diff --git a/packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/TokenInfo.aidl b/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/TokenInfo.aidl
similarity index 100%
rename from packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/TokenInfo.aidl
rename to packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/TokenInfo.aidl
diff --git a/packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/TokenInfo.java b/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/TokenInfo.java
similarity index 100%
rename from packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/TokenInfo.java
rename to packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/TokenInfo.java
diff --git a/packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/utils/BinderUtils.java b/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/utils/BinderUtils.java
similarity index 100%
rename from packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/utils/BinderUtils.java
rename to packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/utils/BinderUtils.java
diff --git a/packages/NeuralNetworks/framework/java/android/service/ondeviceintelligence/IOnDeviceIntelligenceService.aidl b/packages/NeuralNetworks/framework/module/java/android/service/ondeviceintelligence/IOnDeviceIntelligenceService.aidl
similarity index 100%
rename from packages/NeuralNetworks/framework/java/android/service/ondeviceintelligence/IOnDeviceIntelligenceService.aidl
rename to packages/NeuralNetworks/framework/module/java/android/service/ondeviceintelligence/IOnDeviceIntelligenceService.aidl
diff --git a/packages/NeuralNetworks/framework/java/android/service/ondeviceintelligence/IOnDeviceSandboxedInferenceService.aidl b/packages/NeuralNetworks/framework/module/java/android/service/ondeviceintelligence/IOnDeviceSandboxedInferenceService.aidl
similarity index 100%
rename from packages/NeuralNetworks/framework/java/android/service/ondeviceintelligence/IOnDeviceSandboxedInferenceService.aidl
rename to packages/NeuralNetworks/framework/module/java/android/service/ondeviceintelligence/IOnDeviceSandboxedInferenceService.aidl
diff --git a/packages/NeuralNetworks/framework/java/android/service/ondeviceintelligence/IProcessingUpdateStatusCallback.aidl b/packages/NeuralNetworks/framework/module/java/android/service/ondeviceintelligence/IProcessingUpdateStatusCallback.aidl
similarity index 100%
rename from packages/NeuralNetworks/framework/java/android/service/ondeviceintelligence/IProcessingUpdateStatusCallback.aidl
rename to packages/NeuralNetworks/framework/module/java/android/service/ondeviceintelligence/IProcessingUpdateStatusCallback.aidl
diff --git a/packages/NeuralNetworks/framework/java/android/service/ondeviceintelligence/IRemoteProcessingService.aidl b/packages/NeuralNetworks/framework/module/java/android/service/ondeviceintelligence/IRemoteProcessingService.aidl
similarity index 100%
rename from packages/NeuralNetworks/framework/java/android/service/ondeviceintelligence/IRemoteProcessingService.aidl
rename to packages/NeuralNetworks/framework/module/java/android/service/ondeviceintelligence/IRemoteProcessingService.aidl
diff --git a/packages/NeuralNetworks/framework/java/android/service/ondeviceintelligence/IRemoteStorageService.aidl b/packages/NeuralNetworks/framework/module/java/android/service/ondeviceintelligence/IRemoteStorageService.aidl
similarity index 100%
rename from packages/NeuralNetworks/framework/java/android/service/ondeviceintelligence/IRemoteStorageService.aidl
rename to packages/NeuralNetworks/framework/module/java/android/service/ondeviceintelligence/IRemoteStorageService.aidl
diff --git a/packages/NeuralNetworks/framework/java/android/service/ondeviceintelligence/OnDeviceIntelligenceService.java b/packages/NeuralNetworks/framework/module/java/android/service/ondeviceintelligence/OnDeviceIntelligenceService.java
similarity index 100%
rename from packages/NeuralNetworks/framework/java/android/service/ondeviceintelligence/OnDeviceIntelligenceService.java
rename to packages/NeuralNetworks/framework/module/java/android/service/ondeviceintelligence/OnDeviceIntelligenceService.java
diff --git a/packages/NeuralNetworks/framework/java/android/service/ondeviceintelligence/OnDeviceSandboxedInferenceService.java b/packages/NeuralNetworks/framework/module/java/android/service/ondeviceintelligence/OnDeviceSandboxedInferenceService.java
similarity index 100%
rename from packages/NeuralNetworks/framework/java/android/service/ondeviceintelligence/OnDeviceSandboxedInferenceService.java
rename to packages/NeuralNetworks/framework/module/java/android/service/ondeviceintelligence/OnDeviceSandboxedInferenceService.java
diff --git a/packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/DownloadCallback.java b/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/DownloadCallback.java
similarity index 100%
copy from packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/DownloadCallback.java
copy to packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/DownloadCallback.java
diff --git a/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/Feature.aidl b/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/Feature.aidl
new file mode 100644
index 0000000..18494d7
--- /dev/null
+++ b/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/Feature.aidl
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.ondeviceintelligence;
+
+/**
+  * @hide
+  */
+parcelable Feature;
diff --git a/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/Feature.java b/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/Feature.java
new file mode 100644
index 0000000..bcc56073
--- /dev/null
+++ b/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/Feature.java
@@ -0,0 +1,267 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.ondeviceintelligence;
+
+import static android.app.ondeviceintelligence.flags.Flags.FLAG_ENABLE_ON_DEVICE_INTELLIGENCE;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.PersistableBundle;
+
+/**
+ * Represents a typical feature associated with on-device intelligence.
+ *
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(FLAG_ENABLE_ON_DEVICE_INTELLIGENCE)
+public final class Feature implements Parcelable {
+    private final int mId;
+    @Nullable
+    private final String mName;
+    @Nullable
+    private final String mModelName;
+    private final int mType;
+    private final int mVariant;
+    @NonNull
+    private final PersistableBundle mFeatureParams;
+
+    /* package-private */ Feature(
+            int id,
+            @Nullable String name,
+            @Nullable String modelName,
+            int type,
+            int variant,
+            @NonNull PersistableBundle featureParams) {
+        this.mId = id;
+        this.mName = name;
+        this.mModelName = modelName;
+        this.mType = type;
+        this.mVariant = variant;
+        this.mFeatureParams = featureParams;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mFeatureParams);
+    }
+
+    /** Returns the unique and immutable identifier of this feature. */
+    public int getId() {
+        return mId;
+    }
+
+    /** Returns human-readable name of this feature. */
+    public @Nullable String getName() {
+        return mName;
+    }
+
+    /** Returns base model name of this feature. */
+    public @Nullable String getModelName() {
+        return mModelName;
+    }
+
+    /** Returns type identifier of this feature. */
+    public int getType() {
+        return mType;
+    }
+
+    /** Returns variant kind for this feature. */
+    public int getVariant() {
+        return mVariant;
+    }
+
+    public @NonNull PersistableBundle getFeatureParams() {
+        return mFeatureParams;
+    }
+
+    @Override
+    public String toString() {
+        return "Feature { " +
+                "id = " + mId + ", " +
+                "name = " + mName + ", " +
+                "modelName = " + mModelName + ", " +
+                "type = " + mType + ", " +
+                "variant = " + mVariant + ", " +
+                "featureParams = " + mFeatureParams +
+                " }";
+    }
+
+    @Override
+    public boolean equals(@Nullable Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        @SuppressWarnings("unchecked")
+        Feature that = (Feature) o;
+        //noinspection PointlessBooleanExpression
+        return true
+                && mId == that.mId
+                && java.util.Objects.equals(mName, that.mName)
+                && java.util.Objects.equals(mModelName, that.mModelName)
+                && mType == that.mType
+                && mVariant == that.mVariant
+                && java.util.Objects.equals(mFeatureParams, that.mFeatureParams);
+    }
+
+    @Override
+    public int hashCode() {
+        int _hash = 1;
+        _hash = 31 * _hash + mId;
+        _hash = 31 * _hash + java.util.Objects.hashCode(mName);
+        _hash = 31 * _hash + java.util.Objects.hashCode(mModelName);
+        _hash = 31 * _hash + mType;
+        _hash = 31 * _hash + mVariant;
+        _hash = 31 * _hash + java.util.Objects.hashCode(mFeatureParams);
+        return _hash;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        byte flg = 0;
+        if (mName != null) flg |= 0x2;
+        if (mModelName != null) flg |= 0x4;
+        dest.writeByte(flg);
+        dest.writeInt(mId);
+        if (mName != null) dest.writeString(mName);
+        if (mModelName != null) dest.writeString(mModelName);
+        dest.writeInt(mType);
+        dest.writeInt(mVariant);
+        dest.writeTypedObject(mFeatureParams, flags);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /** @hide */
+    @SuppressWarnings({"unchecked", "RedundantCast"})
+    /* package-private */ Feature(@NonNull Parcel in) {
+        byte flg = in.readByte();
+        int id = in.readInt();
+        String name = (flg & 0x2) == 0 ? null : in.readString();
+        String modelName = (flg & 0x4) == 0 ? null : in.readString();
+        int type = in.readInt();
+        int variant = in.readInt();
+        PersistableBundle featureParams = (PersistableBundle) in.readTypedObject(
+                PersistableBundle.CREATOR);
+
+        this.mId = id;
+        this.mName = name;
+        this.mModelName = modelName;
+        this.mType = type;
+        this.mVariant = variant;
+        this.mFeatureParams = featureParams;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mFeatureParams);
+    }
+
+    public static final @NonNull Parcelable.Creator<Feature> CREATOR
+            = new Parcelable.Creator<Feature>() {
+        @Override
+        public Feature[] newArray(int size) {
+            return new Feature[size];
+        }
+
+        @Override
+        public Feature createFromParcel(@NonNull Parcel in) {
+            return new Feature(in);
+        }
+    };
+
+    /**
+     * A builder for {@link Feature}
+     */
+    @SuppressWarnings("WeakerAccess")
+    public static final class Builder {
+        private int mId;
+        private @Nullable String mName;
+        private @Nullable String mModelName;
+        private int mType;
+        private int mVariant;
+        private @NonNull PersistableBundle mFeatureParams;
+
+        private long mBuilderFieldsSet = 0L;
+
+        /**
+         * Provides a builder instance to create a feature for given id.
+         * @param id the unique identifier for the feature.
+         */
+        public Builder(int id) {
+            mId = id;
+            mFeatureParams = new PersistableBundle();
+        }
+
+        public @NonNull Builder setName(@NonNull String value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x2;
+            mName = value;
+            return this;
+        }
+
+        public @NonNull Builder setModelName(@NonNull String value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x4;
+            mModelName = value;
+            return this;
+        }
+
+        public @NonNull Builder setType(int value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x8;
+            mType = value;
+            return this;
+        }
+
+        public @NonNull Builder setVariant(int value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x10;
+            mVariant = value;
+            return this;
+        }
+
+        public @NonNull Builder setFeatureParams(@NonNull PersistableBundle value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x20;
+            mFeatureParams = value;
+            return this;
+        }
+
+        /** Builds the instance. This builder should not be touched after calling this! */
+        public @NonNull Feature build() {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x40; // Mark builder used
+
+            Feature o = new Feature(
+                    mId,
+                    mName,
+                    mModelName,
+                    mType,
+                    mVariant,
+                    mFeatureParams);
+            return o;
+        }
+
+        private void checkNotUsed() {
+            if ((mBuilderFieldsSet & 0x40) != 0) {
+                throw new IllegalStateException(
+                        "This Builder should not be reused. Use a new Builder instance instead");
+            }
+        }
+    }
+}
diff --git a/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/FeatureDetails.aidl b/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/FeatureDetails.aidl
new file mode 100644
index 0000000..0589bf8
--- /dev/null
+++ b/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/FeatureDetails.aidl
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.ondeviceintelligence;
+
+/**
+  * @hide
+  */
+parcelable FeatureDetails;
diff --git a/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/FeatureDetails.java b/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/FeatureDetails.java
new file mode 100644
index 0000000..0ee0cc3
--- /dev/null
+++ b/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/FeatureDetails.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.ondeviceintelligence;
+
+import static android.app.ondeviceintelligence.flags.Flags.FLAG_ENABLE_ON_DEVICE_INTELLIGENCE;
+
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcelable;
+import android.os.PersistableBundle;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.text.MessageFormat;
+
+/**
+ * Represents a status of a requested {@link Feature}.
+ *
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(FLAG_ENABLE_ON_DEVICE_INTELLIGENCE)
+public final class FeatureDetails implements Parcelable {
+    @Status
+    private final int mFeatureStatus;
+    @NonNull
+    private final PersistableBundle mFeatureDetailParams;
+
+    /** Invalid or unavailable {@code AiFeature}. */
+    public static final int FEATURE_STATUS_UNAVAILABLE = 0;
+
+    /** Feature can be downloaded on request. */
+    public static final int FEATURE_STATUS_DOWNLOADABLE = 1;
+
+    /** Feature is being downloaded. */
+    public static final int FEATURE_STATUS_DOWNLOADING = 2;
+
+    /** Feature is fully downloaded and ready to use. */
+    public static final int FEATURE_STATUS_AVAILABLE = 3;
+
+    /** Underlying service is unavailable and feature status cannot be fetched. */
+    public static final int FEATURE_STATUS_SERVICE_UNAVAILABLE = 4;
+
+    /**
+     * @hide
+     */
+    @IntDef(value = {
+            FEATURE_STATUS_UNAVAILABLE,
+            FEATURE_STATUS_DOWNLOADABLE,
+            FEATURE_STATUS_DOWNLOADING,
+            FEATURE_STATUS_AVAILABLE,
+            FEATURE_STATUS_SERVICE_UNAVAILABLE
+    })
+    @Target({ElementType.TYPE_USE, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Status {
+    }
+
+    public FeatureDetails(
+            @Status int featureStatus,
+            @NonNull PersistableBundle featureDetailParams) {
+        this.mFeatureStatus = featureStatus;
+        com.android.internal.util.AnnotationValidations.validate(
+                Status.class, null, mFeatureStatus);
+        this.mFeatureDetailParams = featureDetailParams;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mFeatureDetailParams);
+    }
+
+    public FeatureDetails(
+            @Status int featureStatus) {
+        this.mFeatureStatus = featureStatus;
+        com.android.internal.util.AnnotationValidations.validate(
+                Status.class, null, mFeatureStatus);
+        this.mFeatureDetailParams = new PersistableBundle();
+    }
+
+
+    /**
+     * Returns an integer value associated with the feature status.
+     */
+    public @Status int getFeatureStatus() {
+        return mFeatureStatus;
+    }
+
+
+    /**
+     * Returns a persistable bundle contain any additional status related params.
+     */
+    public @NonNull PersistableBundle getFeatureDetailParams() {
+        return mFeatureDetailParams;
+    }
+
+    @Override
+    public String toString() {
+        return MessageFormat.format("FeatureDetails '{' status = {0}, "
+                        + "persistableBundle = {1} '}'",
+                mFeatureStatus,
+                mFeatureDetailParams);
+    }
+
+    @Override
+    public boolean equals(@android.annotation.Nullable Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        @SuppressWarnings("unchecked")
+        FeatureDetails that = (FeatureDetails) o;
+        return mFeatureStatus == that.mFeatureStatus
+                && java.util.Objects.equals(mFeatureDetailParams, that.mFeatureDetailParams);
+    }
+
+    @Override
+    public int hashCode() {
+        int _hash = 1;
+        _hash = 31 * _hash + mFeatureStatus;
+        _hash = 31 * _hash + java.util.Objects.hashCode(mFeatureDetailParams);
+        return _hash;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+        dest.writeInt(mFeatureStatus);
+        dest.writeTypedObject(mFeatureDetailParams, flags);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /** @hide */
+    @SuppressWarnings({"unchecked", "RedundantCast"})
+    FeatureDetails(@NonNull android.os.Parcel in) {
+        int status = in.readInt();
+        PersistableBundle persistableBundle = (PersistableBundle) in.readTypedObject(
+                PersistableBundle.CREATOR);
+
+        this.mFeatureStatus = status;
+        com.android.internal.util.AnnotationValidations.validate(
+                Status.class, null, mFeatureStatus);
+        this.mFeatureDetailParams = persistableBundle;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mFeatureDetailParams);
+    }
+
+
+    public static final @NonNull Parcelable.Creator<FeatureDetails> CREATOR =
+            new Parcelable.Creator<>() {
+                @Override
+                public FeatureDetails[] newArray(int size) {
+                    return new FeatureDetails[size];
+                }
+
+                @Override
+                public FeatureDetails createFromParcel(@NonNull android.os.Parcel in) {
+                    return new FeatureDetails(in);
+                }
+            };
+
+}
diff --git a/packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/ICancellationSignal.aidl b/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/ICancellationSignal.aidl
similarity index 100%
copy from packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/ICancellationSignal.aidl
copy to packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/ICancellationSignal.aidl
diff --git a/packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/IDownloadCallback.aidl b/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/IDownloadCallback.aidl
similarity index 100%
copy from packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/IDownloadCallback.aidl
copy to packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/IDownloadCallback.aidl
diff --git a/packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/IFeatureCallback.aidl b/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/IFeatureCallback.aidl
similarity index 100%
copy from packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/IFeatureCallback.aidl
copy to packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/IFeatureCallback.aidl
diff --git a/packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/IFeatureDetailsCallback.aidl b/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/IFeatureDetailsCallback.aidl
similarity index 100%
copy from packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/IFeatureDetailsCallback.aidl
copy to packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/IFeatureDetailsCallback.aidl
diff --git a/packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/IListFeaturesCallback.aidl b/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/IListFeaturesCallback.aidl
similarity index 100%
copy from packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/IListFeaturesCallback.aidl
copy to packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/IListFeaturesCallback.aidl
diff --git a/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/IOnDeviceIntelligenceManager.aidl b/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/IOnDeviceIntelligenceManager.aidl
new file mode 100644
index 0000000..1977a39
--- /dev/null
+++ b/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/IOnDeviceIntelligenceManager.aidl
@@ -0,0 +1,79 @@
+/*
+ * 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 android.app.ondeviceintelligence;
+
+ import com.android.internal.infra.AndroidFuture;
+ import android.os.ICancellationSignal;
+ import android.os.ParcelFileDescriptor;
+ import android.os.PersistableBundle;
+ import android.os.RemoteCallback;
+ import android.os.Bundle;
+ import android.app.ondeviceintelligence.Feature;
+ import android.app.ondeviceintelligence.FeatureDetails;
+ import android.app.ondeviceintelligence.InferenceInfo;
+ import java.util.List;
+ import android.app.ondeviceintelligence.IDownloadCallback;
+ import android.app.ondeviceintelligence.IListFeaturesCallback;
+ import android.app.ondeviceintelligence.IFeatureCallback;
+ import android.app.ondeviceintelligence.IFeatureDetailsCallback;
+ import android.app.ondeviceintelligence.IResponseCallback;
+ import android.app.ondeviceintelligence.IStreamingResponseCallback;
+ import android.app.ondeviceintelligence.IProcessingSignal;
+ import android.app.ondeviceintelligence.ITokenInfoCallback;
+
+
+ /**
+  * Interface for a OnDeviceIntelligenceManager for managing OnDeviceIntelligenceService and OnDeviceSandboxedInferenceService.
+  *
+  * @hide
+  */
+interface IOnDeviceIntelligenceManager {
+      @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.USE_ON_DEVICE_INTELLIGENCE)")
+      void getVersion(in RemoteCallback remoteCallback) = 1;
+
+      @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.USE_ON_DEVICE_INTELLIGENCE)")
+      void getFeature(in int featureId, in IFeatureCallback remoteCallback) = 2;
+
+      @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.USE_ON_DEVICE_INTELLIGENCE)")
+      void listFeatures(in IListFeaturesCallback listFeaturesCallback) = 3;
+
+      @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.USE_ON_DEVICE_INTELLIGENCE)")
+      void getFeatureDetails(in Feature feature, in IFeatureDetailsCallback featureDetailsCallback) = 4;
+
+      @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.USE_ON_DEVICE_INTELLIGENCE)")
+      void requestFeatureDownload(in Feature feature, in  AndroidFuture cancellationSignalFuture, in IDownloadCallback callback) = 5;
+
+      @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.USE_ON_DEVICE_INTELLIGENCE)")
+      void requestTokenInfo(in Feature feature, in Bundle requestBundle, in  AndroidFuture cancellationSignalFuture,
+                                                        in ITokenInfoCallback tokenInfocallback) = 6;
+
+      @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.USE_ON_DEVICE_INTELLIGENCE)")
+      void processRequest(in Feature feature, in Bundle requestBundle, int requestType,
+                                                in  AndroidFuture cancellationSignalFuture,
+                                                in AndroidFuture processingSignalFuture,
+                                                in IResponseCallback responseCallback) = 7;
+
+      @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.USE_ON_DEVICE_INTELLIGENCE)")
+      void processRequestStreaming(in Feature feature,
+                    in Bundle requestBundle, int requestType, in  AndroidFuture cancellationSignalFuture,
+                    in  AndroidFuture processingSignalFuture,
+                    in IStreamingResponseCallback streamingCallback) = 8;
+
+      String getRemoteServicePackageName() = 9;
+
+      List<InferenceInfo> getLatestInferenceInfo(long startTimeEpochMillis) = 10;
+ }
diff --git a/packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/IProcessingSignal.aidl b/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/IProcessingSignal.aidl
similarity index 100%
copy from packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/IProcessingSignal.aidl
copy to packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/IProcessingSignal.aidl
diff --git a/packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/IRemoteCallback.aidl b/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/IRemoteCallback.aidl
similarity index 100%
copy from packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/IRemoteCallback.aidl
copy to packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/IRemoteCallback.aidl
diff --git a/packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/IResponseCallback.aidl b/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/IResponseCallback.aidl
similarity index 100%
copy from packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/IResponseCallback.aidl
copy to packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/IResponseCallback.aidl
diff --git a/packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/IStreamingResponseCallback.aidl b/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/IStreamingResponseCallback.aidl
similarity index 100%
copy from packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/IStreamingResponseCallback.aidl
copy to packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/IStreamingResponseCallback.aidl
diff --git a/packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/ITokenInfoCallback.aidl b/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/ITokenInfoCallback.aidl
similarity index 100%
copy from packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/ITokenInfoCallback.aidl
copy to packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/ITokenInfoCallback.aidl
diff --git a/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/InferenceInfo.aidl b/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/InferenceInfo.aidl
new file mode 100644
index 0000000..6d70fc4
--- /dev/null
+++ b/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/InferenceInfo.aidl
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.ondeviceintelligence;
+
+/**
+  * @hide
+  */
+parcelable InferenceInfo;
diff --git a/packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/InferenceInfo.java b/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/InferenceInfo.java
similarity index 100%
copy from packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/InferenceInfo.java
copy to packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/InferenceInfo.java
diff --git a/packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/OnDeviceIntelligenceException.java b/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/OnDeviceIntelligenceException.java
similarity index 100%
copy from packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/OnDeviceIntelligenceException.java
copy to packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/OnDeviceIntelligenceException.java
diff --git a/packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/OnDeviceIntelligenceFrameworkInitializer.java b/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/OnDeviceIntelligenceFrameworkInitializer.java
similarity index 100%
copy from packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/OnDeviceIntelligenceFrameworkInitializer.java
copy to packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/OnDeviceIntelligenceFrameworkInitializer.java
diff --git a/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/OnDeviceIntelligenceManager.java b/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/OnDeviceIntelligenceManager.java
new file mode 100644
index 0000000..78cf1d7
--- /dev/null
+++ b/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/OnDeviceIntelligenceManager.java
@@ -0,0 +1,642 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.ondeviceintelligence;
+
+import static android.app.ondeviceintelligence.flags.Flags.FLAG_ENABLE_ON_DEVICE_INTELLIGENCE;
+import static android.app.ondeviceintelligence.flags.Flags.FLAG_ENABLE_ON_DEVICE_INTELLIGENCE_MODULE;
+
+import android.Manifest;
+import android.annotation.CallbackExecutor;
+import android.annotation.CurrentTimeMillisLong;
+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.SystemService;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.os.IBinder;
+import android.os.ICancellationSignal;
+import android.os.OutcomeReceiver;
+import android.os.PersistableBundle;
+import android.os.RemoteCallback;
+import android.os.RemoteException;
+import android.system.OsConstants;
+import android.util.Log;
+
+import com.android.internal.infra.AndroidFuture;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.util.List;
+import java.util.concurrent.Executor;
+import java.util.function.LongConsumer;
+
+/**
+ * Allows granted apps to manage on-device intelligence service configured on the device. Typical
+ * calling pattern will be to query and setup a required feature before proceeding to request
+ * processing.
+ *
+ * The contracts in this Manager class are designed to be open-ended in general, to allow
+ * interoperability. Therefore, it is recommended that implementations of this system-service
+ * expose this API to the clients via a separate sdk or library which has more defined contract.
+ *
+ * @hide
+ */
+@SystemApi
+@SystemService(Context.ON_DEVICE_INTELLIGENCE_SERVICE)
+@FlaggedApi(FLAG_ENABLE_ON_DEVICE_INTELLIGENCE)
+public final class OnDeviceIntelligenceManager {
+    /**
+     * @hide
+     */
+    public static final String API_VERSION_BUNDLE_KEY = "ApiVersionBundleKey";
+
+    /**
+     * @hide
+     */
+    public static final String AUGMENT_REQUEST_CONTENT_BUNDLE_KEY =
+            "AugmentRequestContentBundleKey";
+
+    private static final String TAG = "OnDeviceIntelligence";
+    private final Context mContext;
+    private final IOnDeviceIntelligenceManager mService;
+
+    /**
+     * @hide
+     */
+    public OnDeviceIntelligenceManager(Context context, IOnDeviceIntelligenceManager service) {
+        mContext = context;
+        mService = service;
+    }
+
+    /**
+     * Asynchronously get the version of the underlying remote implementation.
+     *
+     * @param versionConsumer  consumer to populate the version of remote implementation.
+     * @param callbackExecutor executor to run the callback on.
+     */
+    @RequiresPermission(Manifest.permission.USE_ON_DEVICE_INTELLIGENCE)
+    public void getVersion(
+            @NonNull @CallbackExecutor Executor callbackExecutor,
+            @NonNull LongConsumer versionConsumer) {
+        try {
+            RemoteCallback callback = new RemoteCallback(result -> {
+                if (result == null) {
+                    Binder.withCleanCallingIdentity(
+                            () -> callbackExecutor.execute(() -> versionConsumer.accept(0)));
+                }
+                long version = result.getLong(API_VERSION_BUNDLE_KEY);
+                Binder.withCleanCallingIdentity(
+                        () -> callbackExecutor.execute(() -> versionConsumer.accept(version)));
+            });
+            mService.getVersion(callback);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+
+    /**
+     * Get package name configured for providing the remote implementation for this system service.
+     */
+    @Nullable
+    @RequiresPermission(Manifest.permission.USE_ON_DEVICE_INTELLIGENCE)
+    public String getRemoteServicePackageName() {
+        String result;
+        try {
+            result = mService.getRemoteServicePackageName();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+        return result;
+    }
+
+    /**
+     * Asynchronously get feature for a given id.
+     *
+     * @param featureId        the identifier pointing to the feature.
+     * @param featureReceiver  callback to populate the feature object for given identifier.
+     * @param callbackExecutor executor to run the callback on.
+     */
+    @RequiresPermission(Manifest.permission.USE_ON_DEVICE_INTELLIGENCE)
+    public void getFeature(
+            int featureId,
+            @NonNull @CallbackExecutor Executor callbackExecutor,
+            @NonNull OutcomeReceiver<Feature, OnDeviceIntelligenceException> featureReceiver) {
+        try {
+            IFeatureCallback callback =
+                    new IFeatureCallback.Stub() {
+                        @Override
+                        public void onSuccess(Feature result) {
+                            Binder.withCleanCallingIdentity(() -> callbackExecutor.execute(
+                                    () -> featureReceiver.onResult(result)));
+                        }
+
+                        @Override
+                        public void onFailure(int errorCode, String errorMessage,
+                                PersistableBundle errorParams) {
+                            Binder.withCleanCallingIdentity(() -> callbackExecutor.execute(
+                                    () -> featureReceiver.onError(
+                                            new OnDeviceIntelligenceException(
+                                                    errorCode, errorMessage, errorParams))));
+                        }
+                    };
+            mService.getFeature(featureId, callback);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Asynchronously get a list of features that are supported for the caller.
+     *
+     * @param featureListReceiver callback to populate the list of features.
+     * @param callbackExecutor    executor to run the callback on.
+     */
+    @RequiresPermission(Manifest.permission.USE_ON_DEVICE_INTELLIGENCE)
+    public void listFeatures(
+            @NonNull @CallbackExecutor Executor callbackExecutor,
+            @NonNull OutcomeReceiver<List<Feature>, OnDeviceIntelligenceException> featureListReceiver) {
+        try {
+            IListFeaturesCallback callback =
+                    new IListFeaturesCallback.Stub() {
+                        @Override
+                        public void onSuccess(List<Feature> result) {
+                            Binder.withCleanCallingIdentity(() -> callbackExecutor.execute(
+                                    () -> featureListReceiver.onResult(result)));
+                        }
+
+                        @Override
+                        public void onFailure(int errorCode, String errorMessage,
+                                PersistableBundle errorParams) {
+                            Binder.withCleanCallingIdentity(() -> callbackExecutor.execute(
+                                    () -> featureListReceiver.onError(
+                                            new OnDeviceIntelligenceException(
+                                                    errorCode, errorMessage, errorParams))));
+                        }
+                    };
+            mService.listFeatures(callback);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * This method should be used to fetch details about a feature which need some additional
+     * computation, that can be inefficient to return in all calls to {@link #getFeature}. Callers
+     * and implementation can utilize the {@link Feature#getFeatureParams()} to pass hint on what
+     * details are expected by the caller.
+     *
+     * @param feature                the feature to check status for.
+     * @param featureDetailsReceiver callback to populate the feature details to.
+     * @param callbackExecutor       executor to run the callback on.
+     */
+    @RequiresPermission(Manifest.permission.USE_ON_DEVICE_INTELLIGENCE)
+    public void getFeatureDetails(@NonNull Feature feature,
+            @NonNull @CallbackExecutor Executor callbackExecutor,
+            @NonNull OutcomeReceiver<FeatureDetails, OnDeviceIntelligenceException> featureDetailsReceiver) {
+        try {
+            IFeatureDetailsCallback callback = new IFeatureDetailsCallback.Stub() {
+
+                @Override
+                public void onSuccess(FeatureDetails result) {
+                    Binder.withCleanCallingIdentity(() -> callbackExecutor.execute(
+                            () -> featureDetailsReceiver.onResult(result)));
+                }
+
+                @Override
+                public void onFailure(int errorCode, String errorMessage,
+                        PersistableBundle errorParams) {
+                    Binder.withCleanCallingIdentity(() -> callbackExecutor.execute(
+                            () -> featureDetailsReceiver.onError(
+                                    new OnDeviceIntelligenceException(errorCode,
+                                            errorMessage, errorParams))));
+                }
+            };
+            mService.getFeatureDetails(feature, callback);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * This method handles downloading all model and config files required to process requests
+     * sent against a given feature. The caller can listen to updates on the download status via
+     * the callback.
+     *
+     * Note: If a feature was already requested for downloaded previously, the onDownloadFailed
+     * callback would be invoked with {@link DownloadCallback#DOWNLOAD_FAILURE_STATUS_DOWNLOADING}.
+     * In such cases, clients should query the feature status via {@link #getFeatureDetails} to
+     * check on the feature's download status.
+     *
+     * @param feature            feature to request download for.
+     * @param callback           callback to populate updates about download status.
+     * @param cancellationSignal signal to invoke cancellation on the operation in the remote
+     *                           implementation.
+     * @param callbackExecutor   executor to run the callback on.
+     */
+    @RequiresPermission(Manifest.permission.USE_ON_DEVICE_INTELLIGENCE)
+    public void requestFeatureDownload(@NonNull Feature feature,
+            @Nullable CancellationSignal cancellationSignal,
+            @NonNull @CallbackExecutor Executor callbackExecutor,
+            @NonNull DownloadCallback callback) {
+        try {
+            IDownloadCallback downloadCallback = new IDownloadCallback.Stub() {
+
+                @Override
+                public void onDownloadStarted(long bytesToDownload) {
+                    Binder.withCleanCallingIdentity(() -> callbackExecutor.execute(
+                            () -> callback.onDownloadStarted(bytesToDownload)));
+                }
+
+                @Override
+                public void onDownloadProgress(long bytesDownloaded) {
+                    Binder.withCleanCallingIdentity(() -> callbackExecutor.execute(
+                            () -> callback.onDownloadProgress(bytesDownloaded)));
+                }
+
+                @Override
+                public void onDownloadFailed(int failureStatus, String errorMessage,
+                        PersistableBundle errorParams) {
+                    Binder.withCleanCallingIdentity(() -> callbackExecutor.execute(
+                            () -> callback.onDownloadFailed(failureStatus, errorMessage,
+                                    errorParams)));
+                }
+
+                @Override
+                public void onDownloadCompleted(PersistableBundle downloadParams) {
+                    Binder.withCleanCallingIdentity(() -> callbackExecutor.execute(
+                            () -> callback.onDownloadCompleted(downloadParams)));
+                }
+            };
+
+            mService.requestFeatureDownload(feature,
+                    configureRemoteCancellationFuture(cancellationSignal, callbackExecutor),
+                    downloadCallback);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+
+    /**
+     * The methods computes the token related information for a given request payload using the
+     * provided {@link Feature}.
+     *
+     * @param feature            feature associated with the request.
+     * @param request            request and associated params represented by the Bundle
+     *                           data.
+     * @param outcomeReceiver    callback to populate the token info or exception in case of
+     *                           failure.
+     * @param cancellationSignal signal to invoke cancellation on the operation in the remote
+     *                           implementation.
+     * @param callbackExecutor   executor to run the callback on.
+     */
+    @RequiresPermission(Manifest.permission.USE_ON_DEVICE_INTELLIGENCE)
+    public void requestTokenInfo(@NonNull Feature feature, @NonNull @InferenceParams Bundle request,
+            @Nullable CancellationSignal cancellationSignal,
+            @NonNull @CallbackExecutor Executor callbackExecutor,
+            @NonNull OutcomeReceiver<TokenInfo,
+                    OnDeviceIntelligenceException> outcomeReceiver) {
+        try {
+            ITokenInfoCallback callback = new ITokenInfoCallback.Stub() {
+                @Override
+                public void onSuccess(TokenInfo tokenInfo) {
+                    Binder.withCleanCallingIdentity(() -> callbackExecutor.execute(
+                            () -> outcomeReceiver.onResult(tokenInfo)));
+                }
+
+                @Override
+                public void onFailure(int errorCode, String errorMessage,
+                        PersistableBundle errorParams) {
+                    Binder.withCleanCallingIdentity(() -> callbackExecutor.execute(
+                            () -> outcomeReceiver.onError(
+                                    new OnDeviceIntelligenceException(
+                                            errorCode, errorMessage, errorParams))));
+                }
+            };
+
+            mService.requestTokenInfo(feature, request,
+                    configureRemoteCancellationFuture(cancellationSignal, callbackExecutor),
+                    callback);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+
+    /**
+     * Asynchronously Process a request based on the associated params, to populate a
+     * response in
+     * {@link OutcomeReceiver#onResult} callback or failure callback status code if there
+     * was a
+     * failure.
+     *
+     * @param feature            feature associated with the request.
+     * @param request            request and associated params represented by the Bundle
+     *                           data.
+     * @param requestType        type of request being sent for processing the content.
+     * @param cancellationSignal signal to invoke cancellation.
+     * @param processingSignal   signal to send custom signals in the
+     *                           remote implementation.
+     * @param callbackExecutor   executor to run the callback on.
+     * @param processingCallback callback to populate the response content and
+     *                           associated params.
+     */
+    @RequiresPermission(Manifest.permission.USE_ON_DEVICE_INTELLIGENCE)
+    public void processRequest(@NonNull Feature feature, @NonNull @InferenceParams Bundle request,
+            @RequestType int requestType,
+            @Nullable CancellationSignal cancellationSignal,
+            @Nullable ProcessingSignal processingSignal,
+            @NonNull @CallbackExecutor Executor callbackExecutor,
+            @NonNull ProcessingCallback processingCallback) {
+        try {
+            IResponseCallback callback = new IResponseCallback.Stub() {
+                @Override
+                public void onSuccess(@InferenceParams Bundle result) {
+                    Binder.withCleanCallingIdentity(() -> {
+                        callbackExecutor.execute(() -> processingCallback.onResult(result));
+                    });
+                }
+
+                @Override
+                public void onFailure(int errorCode, String errorMessage,
+                        PersistableBundle errorParams) {
+                    Binder.withCleanCallingIdentity(() -> callbackExecutor.execute(
+                            () -> processingCallback.onError(
+                                    new OnDeviceIntelligenceException(
+                                            errorCode, errorMessage, errorParams))));
+                }
+
+                @Override
+                public void onDataAugmentRequest(@NonNull @InferenceParams Bundle request,
+                        @NonNull RemoteCallback contentCallback) {
+                    Binder.withCleanCallingIdentity(() -> callbackExecutor.execute(
+                            () -> processingCallback.onDataAugmentRequest(request, result -> {
+                                Bundle bundle = new Bundle();
+                                bundle.putParcelable(AUGMENT_REQUEST_CONTENT_BUNDLE_KEY, result);
+                                callbackExecutor.execute(() -> contentCallback.sendResult(bundle));
+                            })));
+                }
+            };
+
+
+            mService.processRequest(feature, request, requestType,
+                    configureRemoteCancellationFuture(cancellationSignal, callbackExecutor),
+                    configureRemoteProcessingSignalFuture(processingSignal, callbackExecutor),
+                    callback);
+
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Variation of {@link #processRequest} that asynchronously processes a request in a
+     * streaming
+     * fashion, where new content is pushed to caller in chunks via the
+     * {@link StreamingProcessingCallback#onPartialResult}. After the streaming is complete,
+     * the service should call {@link StreamingProcessingCallback#onResult} and can optionally
+     * populate the complete the full response {@link Bundle} as part of the callback in cases
+     * when the final response contains an enhanced aggregation of the contents already
+     * streamed.
+     *
+     * @param feature                     feature associated with the request.
+     * @param request                     request and associated params represented by the Bundle
+     *                                    data.
+     * @param requestType                 type of request being sent for processing the content.
+     * @param cancellationSignal          signal to invoke cancellation.
+     * @param processingSignal            signal to send custom signals in the
+     *                                    remote implementation.
+     * @param streamingProcessingCallback streaming callback to populate the response content and
+     *                                    associated params.
+     * @param callbackExecutor            executor to run the callback on.
+     */
+    @RequiresPermission(Manifest.permission.USE_ON_DEVICE_INTELLIGENCE)
+    public void processRequestStreaming(@NonNull Feature feature,
+            @NonNull @InferenceParams Bundle request,
+            @RequestType int requestType,
+            @Nullable CancellationSignal cancellationSignal,
+            @Nullable ProcessingSignal processingSignal,
+            @NonNull @CallbackExecutor Executor callbackExecutor,
+            @NonNull StreamingProcessingCallback streamingProcessingCallback) {
+        try {
+            IStreamingResponseCallback callback = new IStreamingResponseCallback.Stub() {
+                @Override
+                public void onNewContent(@InferenceParams Bundle result) {
+                    Binder.withCleanCallingIdentity(() -> {
+                        callbackExecutor.execute(
+                                () -> streamingProcessingCallback.onPartialResult(result));
+                    });
+                }
+
+                @Override
+                public void onSuccess(@InferenceParams Bundle result) {
+                    Binder.withCleanCallingIdentity(() -> {
+                        callbackExecutor.execute(
+                                () -> streamingProcessingCallback.onResult(result));
+                    });
+                }
+
+                @Override
+                public void onFailure(int errorCode, String errorMessage,
+                        PersistableBundle errorParams) {
+                    Binder.withCleanCallingIdentity(() -> {
+                        callbackExecutor.execute(
+                                () -> streamingProcessingCallback.onError(
+                                        new OnDeviceIntelligenceException(
+                                                errorCode, errorMessage, errorParams)));
+                    });
+                }
+
+
+                @Override
+                public void onDataAugmentRequest(@NonNull @InferenceParams Bundle content,
+                        @NonNull RemoteCallback contentCallback) {
+                    Binder.withCleanCallingIdentity(() -> callbackExecutor.execute(
+                            () -> streamingProcessingCallback.onDataAugmentRequest(content,
+                                    contentResponse -> {
+                                        Bundle bundle = new Bundle();
+                                        bundle.putParcelable(AUGMENT_REQUEST_CONTENT_BUNDLE_KEY,
+                                                contentResponse);
+                                        callbackExecutor.execute(
+                                                () -> contentCallback.sendResult(bundle));
+                                    })));
+                }
+            };
+
+            mService.processRequestStreaming(
+                    feature, request, requestType,
+                    configureRemoteCancellationFuture(cancellationSignal, callbackExecutor),
+                    configureRemoteProcessingSignalFuture(processingSignal, callbackExecutor),
+                    callback);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * This is primarily intended to be used to attribute/blame on-device intelligence power usage,
+     * via the configured remote implementation, to its actual caller.
+     *
+     * @param startTimeEpochMillis epoch millis used to filter the InferenceInfo events.
+     * @return InferenceInfo events since the passed in startTimeEpochMillis.
+     */
+    @RequiresPermission(Manifest.permission.DUMP)
+    @FlaggedApi(FLAG_ENABLE_ON_DEVICE_INTELLIGENCE_MODULE)
+    public @NonNull List<InferenceInfo> getLatestInferenceInfo(@CurrentTimeMillisLong long startTimeEpochMillis) {
+        try {
+            return mService.getLatestInferenceInfo(startTimeEpochMillis);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+
+    /** Request inference with provided Bundle and Params. */
+    public static final int REQUEST_TYPE_INFERENCE = 0;
+
+    /**
+     * Prepares the remote implementation environment for e.g.loading inference runtime etc
+     * .which
+     * are time consuming beforehand to remove overhead and allow quick processing of requests
+     * thereof.
+     */
+    public static final int REQUEST_TYPE_PREPARE = 1;
+
+    /** Request Embeddings of the passed-in Bundle. */
+    public static final int REQUEST_TYPE_EMBEDDINGS = 2;
+
+    /**
+     * @hide
+     */
+    @IntDef(value = {
+            REQUEST_TYPE_INFERENCE,
+            REQUEST_TYPE_PREPARE,
+            REQUEST_TYPE_EMBEDDINGS
+    })
+    @Target({ElementType.TYPE_USE, ElementType.METHOD, ElementType.PARAMETER,
+            ElementType.FIELD})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface RequestType {
+    }
+
+    /**
+     * {@link Bundle}s annotated with this type will be validated that they are in-effect read-only
+     * when passed via Binder IPC. Following restrictions apply :
+     * <ul>
+     * <li> {@link PersistableBundle}s are allowed.</li>
+     * <li> Any primitive types or their collections can be added as usual.</li>
+     * <li>IBinder objects should *not* be added.</li>
+     * <li>Parcelable data which has no active-objects, should be added as
+     * {@link Bundle#putByteArray}</li>
+     * <li>Parcelables have active-objects, only following types will be allowed</li>
+     * <ul>
+     *  <li>{@link android.os.ParcelFileDescriptor} opened in
+     *  {@link android.os.ParcelFileDescriptor#MODE_READ_ONLY}</li>
+     * </ul>
+     * </ul>
+     *
+     * In all other scenarios the system-server might throw a
+     * {@link android.os.BadParcelableException} if the Bundle validation fails.
+     *
+     * @hide
+     */
+    @Target({ElementType.PARAMETER, ElementType.FIELD})
+    public @interface StateParams {
+    }
+
+    /**
+     * This is an extension of {@link StateParams} but for purpose of inference few other types are
+     * also allowed as read-only, as listed below.
+     *
+     * <li>{@link Bitmap} set as immutable.</li>
+     * <li>{@link android.database.CursorWindow}</li>
+     * <li>{@link android.os.SharedMemory} set to {@link OsConstants#PROT_READ}</li>
+     * </ul>
+     * </ul>
+     *
+     * In all other scenarios the system-server might throw a
+     * {@link android.os.BadParcelableException} if the Bundle validation fails.
+     *
+     * @hide
+     */
+    @Target({ElementType.PARAMETER, ElementType.FIELD, ElementType.TYPE_USE})
+    public @interface InferenceParams {
+    }
+
+    /**
+     * This is an extension of {@link StateParams} with the exception that it allows writing
+     * {@link Bitmap} as part of the response.
+     *
+     * In all other scenarios the system-server might throw a
+     * {@link android.os.BadParcelableException} if the Bundle validation fails.
+     *
+     * @hide
+     */
+    @Target({ElementType.PARAMETER, ElementType.FIELD})
+    public @interface ResponseParams {
+    }
+
+    @Nullable
+    private static AndroidFuture<IBinder> configureRemoteCancellationFuture(
+            @Nullable CancellationSignal cancellationSignal,
+            @NonNull Executor callbackExecutor) {
+        if (cancellationSignal == null) {
+            return null;
+        }
+        AndroidFuture<IBinder> cancellationFuture = new AndroidFuture<>();
+        cancellationFuture.whenCompleteAsync(
+                (cancellationTransport, error) -> {
+                    if (error != null || cancellationTransport == null) {
+                        Log.e(TAG, "Unable to receive the remote cancellation signal.", error);
+                    } else {
+                        cancellationSignal.setRemote(
+                                ICancellationSignal.Stub.asInterface(cancellationTransport));
+                    }
+                }, callbackExecutor);
+        return cancellationFuture;
+    }
+
+    @Nullable
+    private static AndroidFuture<IBinder> configureRemoteProcessingSignalFuture(
+            ProcessingSignal processingSignal, Executor executor) {
+        if (processingSignal == null) {
+            return null;
+        }
+        AndroidFuture<IBinder> processingSignalFuture = new AndroidFuture<>();
+        processingSignalFuture.whenCompleteAsync(
+                (transport, error) -> {
+                    if (error != null || transport == null) {
+                        Log.e(TAG, "Unable to receive the remote processing signal.", error);
+                    } else {
+                        processingSignal.setRemote(IProcessingSignal.Stub.asInterface(transport));
+                    }
+                }, executor);
+        return processingSignalFuture;
+    }
+
+
+}
diff --git a/packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/ProcessingCallback.java b/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/ProcessingCallback.java
similarity index 100%
copy from packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/ProcessingCallback.java
copy to packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/ProcessingCallback.java
diff --git a/packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/ProcessingSignal.java b/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/ProcessingSignal.java
similarity index 100%
copy from packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/ProcessingSignal.java
copy to packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/ProcessingSignal.java
diff --git a/packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/StreamingProcessingCallback.java b/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/StreamingProcessingCallback.java
similarity index 100%
copy from packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/StreamingProcessingCallback.java
copy to packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/StreamingProcessingCallback.java
diff --git a/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/TokenInfo.aidl b/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/TokenInfo.aidl
new file mode 100644
index 0000000..2c19c1e
--- /dev/null
+++ b/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/TokenInfo.aidl
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.ondeviceintelligence;
+
+/**
+  * @hide
+  */
+parcelable TokenInfo;
diff --git a/packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/TokenInfo.java b/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/TokenInfo.java
similarity index 100%
copy from packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/TokenInfo.java
copy to packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/TokenInfo.java
diff --git a/packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/utils/BinderUtils.java b/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/utils/BinderUtils.java
similarity index 100%
copy from packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/utils/BinderUtils.java
copy to packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/utils/BinderUtils.java
diff --git a/packages/NeuralNetworks/framework/platform/java/android/service/ondeviceintelligence/IOnDeviceIntelligenceService.aidl b/packages/NeuralNetworks/framework/platform/java/android/service/ondeviceintelligence/IOnDeviceIntelligenceService.aidl
new file mode 100644
index 0000000..45c4350
--- /dev/null
+++ b/packages/NeuralNetworks/framework/platform/java/android/service/ondeviceintelligence/IOnDeviceIntelligenceService.aidl
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.service.ondeviceintelligence;
+
+import android.os.PersistableBundle;
+import android.os.ParcelFileDescriptor;
+import android.os.ICancellationSignal;
+import android.os.RemoteCallback;
+import android.app.ondeviceintelligence.IDownloadCallback;
+import android.app.ondeviceintelligence.Feature;
+import android.app.ondeviceintelligence.IFeatureCallback;
+import android.app.ondeviceintelligence.IListFeaturesCallback;
+import android.app.ondeviceintelligence.IFeatureDetailsCallback;
+import com.android.internal.infra.AndroidFuture;
+import android.service.ondeviceintelligence.IRemoteProcessingService;
+
+
+/**
+ * Interface for a concrete implementation to provide on device intelligence services.
+ *
+ * @hide
+ */
+oneway interface IOnDeviceIntelligenceService {
+    void getVersion(in RemoteCallback remoteCallback);
+    void getFeature(int callerUid, int featureId, in IFeatureCallback featureCallback);
+    void listFeatures(int callerUid, in IListFeaturesCallback listFeaturesCallback);
+    void getFeatureDetails(int callerUid, in Feature feature, in IFeatureDetailsCallback featureDetailsCallback);
+    void getReadOnlyFileDescriptor(in String fileName, in AndroidFuture<ParcelFileDescriptor> future);
+    void getReadOnlyFeatureFileDescriptorMap(in Feature feature, in RemoteCallback remoteCallback);
+    void requestFeatureDownload(int callerUid, in Feature feature,
+                                in AndroidFuture cancellationSignal,
+                                in IDownloadCallback downloadCallback);
+    void registerRemoteServices(in IRemoteProcessingService remoteProcessingService);
+    void notifyInferenceServiceConnected();
+    void notifyInferenceServiceDisconnected();
+    void ready();
+}
\ No newline at end of file
diff --git a/packages/NeuralNetworks/framework/platform/java/android/service/ondeviceintelligence/IOnDeviceSandboxedInferenceService.aidl b/packages/NeuralNetworks/framework/platform/java/android/service/ondeviceintelligence/IOnDeviceSandboxedInferenceService.aidl
new file mode 100644
index 0000000..1af3b0f
--- /dev/null
+++ b/packages/NeuralNetworks/framework/platform/java/android/service/ondeviceintelligence/IOnDeviceSandboxedInferenceService.aidl
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.service.ondeviceintelligence;
+
+import android.app.ondeviceintelligence.IStreamingResponseCallback;
+import android.app.ondeviceintelligence.IResponseCallback;
+import android.app.ondeviceintelligence.ITokenInfoCallback;
+import android.app.ondeviceintelligence.IProcessingSignal;
+import android.app.ondeviceintelligence.Feature;
+import android.os.IRemoteCallback;
+import android.os.ICancellationSignal;
+import android.os.PersistableBundle;
+import android.os.Bundle;
+import com.android.internal.infra.AndroidFuture;
+import android.service.ondeviceintelligence.IRemoteStorageService;
+import android.service.ondeviceintelligence.IProcessingUpdateStatusCallback;
+
+/**
+ * Interface for a concrete implementation to provide on-device sandboxed inference.
+ *
+ * @hide
+ */
+oneway interface IOnDeviceSandboxedInferenceService {
+    void registerRemoteStorageService(in IRemoteStorageService storageService,
+                                        in IRemoteCallback remoteCallback) = 0;
+    void requestTokenInfo(int callerUid, in Feature feature, in Bundle request,
+                            in AndroidFuture cancellationSignal,
+                            in ITokenInfoCallback tokenInfoCallback) = 1;
+    void processRequest(int callerUid, in Feature feature, in Bundle request, in int requestType,
+                        in AndroidFuture cancellationSignal,
+                        in AndroidFuture processingSignal,
+                        in IResponseCallback callback) = 2;
+    void processRequestStreaming(int callerUid, in Feature feature, in Bundle request, in int requestType,
+                                in AndroidFuture cancellationSignal,
+                                in AndroidFuture processingSignal,
+                                in IStreamingResponseCallback callback) = 3;
+    void updateProcessingState(in Bundle processingState,
+                                     in IProcessingUpdateStatusCallback callback) = 4;
+}
\ No newline at end of file
diff --git a/packages/NeuralNetworks/framework/java/android/service/ondeviceintelligence/IProcessingUpdateStatusCallback.aidl b/packages/NeuralNetworks/framework/platform/java/android/service/ondeviceintelligence/IProcessingUpdateStatusCallback.aidl
similarity index 100%
copy from packages/NeuralNetworks/framework/java/android/service/ondeviceintelligence/IProcessingUpdateStatusCallback.aidl
copy to packages/NeuralNetworks/framework/platform/java/android/service/ondeviceintelligence/IProcessingUpdateStatusCallback.aidl
diff --git a/packages/NeuralNetworks/framework/java/android/service/ondeviceintelligence/IRemoteProcessingService.aidl b/packages/NeuralNetworks/framework/platform/java/android/service/ondeviceintelligence/IRemoteProcessingService.aidl
similarity index 100%
copy from packages/NeuralNetworks/framework/java/android/service/ondeviceintelligence/IRemoteProcessingService.aidl
copy to packages/NeuralNetworks/framework/platform/java/android/service/ondeviceintelligence/IRemoteProcessingService.aidl
diff --git a/packages/NeuralNetworks/framework/platform/java/android/service/ondeviceintelligence/IRemoteStorageService.aidl b/packages/NeuralNetworks/framework/platform/java/android/service/ondeviceintelligence/IRemoteStorageService.aidl
new file mode 100644
index 0000000..a6f49e1
--- /dev/null
+++ b/packages/NeuralNetworks/framework/platform/java/android/service/ondeviceintelligence/IRemoteStorageService.aidl
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.service.ondeviceintelligence;
+
+import android.app.ondeviceintelligence.Feature;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteCallback;
+
+import com.android.internal.infra.AndroidFuture;
+
+/**
+ * Interface for a concrete implementation to provide access to storage read access
+ * for the isolated process.
+ *
+ * @hide
+ */
+oneway interface IRemoteStorageService {
+    void getReadOnlyFileDescriptor(in String filePath, in AndroidFuture<ParcelFileDescriptor> future);
+    void getReadOnlyFeatureFileDescriptorMap(in Feature feature, in RemoteCallback remoteCallback);
+}
\ No newline at end of file
diff --git a/packages/NeuralNetworks/framework/platform/java/android/service/ondeviceintelligence/OnDeviceIntelligenceService.java b/packages/NeuralNetworks/framework/platform/java/android/service/ondeviceintelligence/OnDeviceIntelligenceService.java
new file mode 100644
index 0000000..618d2a0
--- /dev/null
+++ b/packages/NeuralNetworks/framework/platform/java/android/service/ondeviceintelligence/OnDeviceIntelligenceService.java
@@ -0,0 +1,552 @@
+/*
+ * 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 android.service.ondeviceintelligence;
+
+import static android.app.ondeviceintelligence.flags.Flags.FLAG_ENABLE_ON_DEVICE_INTELLIGENCE;
+
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+
+import android.annotation.CallSuper;
+import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SdkConstant;
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.app.ondeviceintelligence.DownloadCallback;
+import android.app.ondeviceintelligence.Feature;
+import android.app.ondeviceintelligence.FeatureDetails;
+import android.app.ondeviceintelligence.IDownloadCallback;
+import android.app.ondeviceintelligence.IFeatureCallback;
+import android.app.ondeviceintelligence.IFeatureDetailsCallback;
+import android.app.ondeviceintelligence.IListFeaturesCallback;
+import android.app.ondeviceintelligence.OnDeviceIntelligenceException;
+import android.app.ondeviceintelligence.OnDeviceIntelligenceManager;
+import android.app.ondeviceintelligence.OnDeviceIntelligenceManager.StateParams;
+import android.content.Intent;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.ICancellationSignal;
+import android.os.Looper;
+import android.os.OutcomeReceiver;
+import android.os.ParcelFileDescriptor;
+import android.os.PersistableBundle;
+import android.os.RemoteCallback;
+import android.os.RemoteException;
+import android.util.Log;
+import android.util.Slog;
+
+import com.android.internal.infra.AndroidFuture;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
+import java.util.function.LongConsumer;
+
+/**
+ * Abstract base class for performing setup for on-device inference and providing file access to
+ * the isolated counter part {@link OnDeviceSandboxedInferenceService}.
+ *
+ * <p> A service that provides configuration and model files relevant to performing inference on
+ * device. The system's default OnDeviceIntelligenceService implementation is configured in
+ * {@code config_defaultOnDeviceIntelligenceService}. If this config has no value, a stub is
+ * returned.
+ *
+ * <p> Similar to {@link OnDeviceIntelligenceManager} class, the contracts in this service are
+ * defined to be open-ended in general, to allow interoperability. Therefore, it is recommended
+ * that implementations of this system-service expose this API to the clients via a library which
+ * has more defined contract.</p>
+ * <pre>
+ * {@literal
+ * <service android:name=".SampleOnDeviceIntelligenceService"
+ *          android:permission="android.permission.BIND_ON_DEVICE_INTELLIGENCE_SERVICE">
+ * </service>}
+ * </pre>
+ *
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(FLAG_ENABLE_ON_DEVICE_INTELLIGENCE)
+public abstract class OnDeviceIntelligenceService extends Service {
+    private static final String TAG = OnDeviceIntelligenceService.class.getSimpleName();
+
+    private volatile IRemoteProcessingService mRemoteProcessingService;
+    private Handler mHandler;
+
+    @CallSuper
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        mHandler = new Handler(Looper.getMainLooper(), null /* callback */, true /* async */);
+    }
+
+    /**
+     * 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_ON_DEVICE_INTELLIGENCE_SERVICE}
+     * permission so that other applications can not abuse it.
+     */
+    @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
+    public static final String SERVICE_INTERFACE =
+            "android.service.ondeviceintelligence.OnDeviceIntelligenceService";
+
+
+    /**
+     * @hide
+     */
+    @Nullable
+    @Override
+    public final IBinder onBind(@NonNull Intent intent) {
+        if (SERVICE_INTERFACE.equals(intent.getAction())) {
+            return new IOnDeviceIntelligenceService.Stub() {
+                /** {@inheritDoc} */
+                @Override
+                public void ready() {
+                    mHandler.executeOrSendMessage(
+                            obtainMessage(OnDeviceIntelligenceService::onReady,
+                                    OnDeviceIntelligenceService.this));
+                }
+
+                @Override
+                public void getVersion(RemoteCallback remoteCallback) {
+                    Objects.requireNonNull(remoteCallback);
+                    mHandler.executeOrSendMessage(
+                            obtainMessage(
+                                    OnDeviceIntelligenceService::onGetVersion,
+                                    OnDeviceIntelligenceService.this, l -> {
+                                        Bundle b = new Bundle();
+                                        b.putLong(
+                                                OnDeviceIntelligenceManager.API_VERSION_BUNDLE_KEY,
+                                                l);
+                                        remoteCallback.sendResult(b);
+                                    }));
+                }
+
+                @Override
+                public void listFeatures(int callerUid,
+                        IListFeaturesCallback listFeaturesCallback) {
+                    Objects.requireNonNull(listFeaturesCallback);
+                    mHandler.executeOrSendMessage(
+                            obtainMessage(
+                                    OnDeviceIntelligenceService::onListFeatures,
+                                    OnDeviceIntelligenceService.this, callerUid,
+                                    wrapListFeaturesCallback(listFeaturesCallback)));
+                }
+
+                @Override
+                public void getFeature(int callerUid, int id, IFeatureCallback featureCallback) {
+                    Objects.requireNonNull(featureCallback);
+                    mHandler.executeOrSendMessage(
+                            obtainMessage(
+                                    OnDeviceIntelligenceService::onGetFeature,
+                                    OnDeviceIntelligenceService.this, callerUid,
+                                    id, wrapFeatureCallback(featureCallback)));
+                }
+
+
+                @Override
+                public void getFeatureDetails(int callerUid, Feature feature,
+                        IFeatureDetailsCallback featureDetailsCallback) {
+                    Objects.requireNonNull(feature);
+                    Objects.requireNonNull(featureDetailsCallback);
+                    mHandler.executeOrSendMessage(
+                            obtainMessage(
+                                    OnDeviceIntelligenceService::onGetFeatureDetails,
+                                    OnDeviceIntelligenceService.this, callerUid,
+                                    feature, wrapFeatureDetailsCallback(featureDetailsCallback)));
+                }
+
+                @Override
+                public void requestFeatureDownload(int callerUid, Feature feature,
+                        AndroidFuture cancellationSignalFuture,
+                        IDownloadCallback downloadCallback) {
+                    Objects.requireNonNull(feature);
+                    Objects.requireNonNull(downloadCallback);
+                    ICancellationSignal transport = null;
+                    if (cancellationSignalFuture != null) {
+                        transport = CancellationSignal.createTransport();
+                        cancellationSignalFuture.complete(transport);
+                    }
+                    mHandler.executeOrSendMessage(
+                            obtainMessage(
+                                    OnDeviceIntelligenceService::onDownloadFeature,
+                                    OnDeviceIntelligenceService.this, callerUid,
+                                    feature,
+                                    CancellationSignal.fromTransport(transport),
+                                    wrapDownloadCallback(downloadCallback)));
+                }
+
+                @Override
+                public void getReadOnlyFileDescriptor(String fileName,
+                        AndroidFuture<ParcelFileDescriptor> future) {
+                    Objects.requireNonNull(fileName);
+                    Objects.requireNonNull(future);
+                    mHandler.executeOrSendMessage(
+                            obtainMessage(
+                                    OnDeviceIntelligenceService::onGetReadOnlyFileDescriptor,
+                                    OnDeviceIntelligenceService.this, fileName,
+                                    future));
+                }
+
+                @Override
+                public void getReadOnlyFeatureFileDescriptorMap(
+                        Feature feature, RemoteCallback remoteCallback) {
+                    Objects.requireNonNull(feature);
+                    Objects.requireNonNull(remoteCallback);
+                    mHandler.executeOrSendMessage(
+                            obtainMessage(
+                                    OnDeviceIntelligenceService::onGetReadOnlyFeatureFileDescriptorMap,
+                                    OnDeviceIntelligenceService.this, feature,
+                                    parcelFileDescriptorMap -> {
+                                        Bundle bundle = new Bundle();
+                                        parcelFileDescriptorMap.forEach(bundle::putParcelable);
+                                        remoteCallback.sendResult(bundle);
+                                        tryClosePfds(parcelFileDescriptorMap.values());
+                                    }));
+                }
+
+                @Override
+                public void registerRemoteServices(
+                        IRemoteProcessingService remoteProcessingService) {
+                    mRemoteProcessingService = remoteProcessingService;
+                }
+
+                @Override
+                public void notifyInferenceServiceConnected() {
+                    mHandler.executeOrSendMessage(
+                            obtainMessage(
+                                    OnDeviceIntelligenceService::onInferenceServiceConnected,
+                                    OnDeviceIntelligenceService.this));
+                }
+
+                @Override
+                public void notifyInferenceServiceDisconnected() {
+                    mHandler.executeOrSendMessage(
+                            obtainMessage(
+                                    OnDeviceIntelligenceService::onInferenceServiceDisconnected,
+                                    OnDeviceIntelligenceService.this));
+                }
+            };
+        }
+        Slog.w(TAG, "Incorrect service interface, returning null.");
+        return null;
+    }
+
+    /**
+     * Using this signal to assertively a signal each time service binds successfully, used only in
+     * tests to get a signal that service instance is ready. This is needed because we cannot rely
+     * on {@link #onCreate} or {@link #onBind} to be invoke on each binding.
+     */
+    public void onReady() {
+    }
+
+
+    /**
+     * Invoked when a new instance of the remote inference service is created.
+     * This method should be used as a signal to perform any initialization operations, for e.g. by
+     * invoking the {@link #updateProcessingState} method to initialize the remote processing
+     * service.
+     */
+    public abstract void onInferenceServiceConnected();
+
+
+    /**
+     * Invoked when an instance of the remote inference service is disconnected.
+     */
+    public abstract void onInferenceServiceDisconnected();
+
+
+    /**
+     * Invoked by the {@link OnDeviceIntelligenceService} inorder to send updates to the inference
+     * service if there is a state change to be performed. State change could be config updates,
+     * performing initialization or cleanup tasks in the remote inference service.
+     * The Bundle passed in here is expected to be read-only and will be rejected if it has any
+     * writable fields as detailed under {@link StateParams}.
+     *
+     * @param processingState  the updated state to be applied.
+     * @param callbackExecutor executor to the run status callback on.
+     * @param statusReceiver   receiver to get status of the update state operation.
+     */
+    public final void updateProcessingState(@NonNull @StateParams Bundle processingState,
+            @NonNull @CallbackExecutor Executor callbackExecutor,
+            @NonNull OutcomeReceiver<PersistableBundle, OnDeviceIntelligenceException> statusReceiver) {
+        Objects.requireNonNull(callbackExecutor);
+        if (mRemoteProcessingService == null) {
+            throw new IllegalStateException("Remote processing service is unavailable.");
+        }
+        try {
+            mRemoteProcessingService.updateProcessingState(processingState,
+                    new IProcessingUpdateStatusCallback.Stub() {
+                        @Override
+                        public void onSuccess(PersistableBundle result) {
+                            Binder.withCleanCallingIdentity(() -> {
+                                callbackExecutor.execute(
+                                        () -> statusReceiver.onResult(result));
+                            });
+                        }
+
+                        @Override
+                        public void onFailure(int errorCode, String errorMessage) {
+                            Binder.withCleanCallingIdentity(() -> callbackExecutor.execute(
+                                    () -> statusReceiver.onError(
+                                            new OnDeviceIntelligenceException(
+                                                    errorCode, errorMessage))));
+                        }
+                    });
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Error in updateProcessingState: " + e);
+            throw new RuntimeException(e);
+        }
+    }
+
+    private OutcomeReceiver<Feature,
+            OnDeviceIntelligenceException> wrapFeatureCallback(
+            IFeatureCallback featureCallback) {
+        return new OutcomeReceiver<>() {
+            @Override
+            public void onResult(@NonNull Feature feature) {
+                try {
+                    featureCallback.onSuccess(feature);
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Error sending feature: " + e);
+                }
+            }
+
+            @Override
+            public void onError(
+                    @NonNull OnDeviceIntelligenceException exception) {
+                try {
+                    featureCallback.onFailure(exception.getErrorCode(), exception.getMessage(),
+                            exception.getErrorParams());
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Error sending download feature: " + e);
+                }
+            }
+        };
+    }
+
+    private OutcomeReceiver<List<Feature>,
+            OnDeviceIntelligenceException> wrapListFeaturesCallback(
+            IListFeaturesCallback listFeaturesCallback) {
+        return new OutcomeReceiver<>() {
+            @Override
+            public void onResult(@NonNull List<Feature> features) {
+                try {
+                    listFeaturesCallback.onSuccess(features);
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Error sending feature: " + e);
+                }
+            }
+
+            @Override
+            public void onError(
+                    @NonNull OnDeviceIntelligenceException exception) {
+                try {
+                    listFeaturesCallback.onFailure(exception.getErrorCode(), exception.getMessage(),
+                            exception.getErrorParams());
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Error sending download feature: " + e);
+                }
+            }
+        };
+    }
+
+    private OutcomeReceiver<FeatureDetails,
+            OnDeviceIntelligenceException> wrapFeatureDetailsCallback(
+            IFeatureDetailsCallback featureStatusCallback) {
+        return new OutcomeReceiver<>() {
+            @Override
+            public void onResult(FeatureDetails result) {
+                try {
+                    featureStatusCallback.onSuccess(result);
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Error sending feature status: " + e);
+                }
+            }
+
+            @Override
+            public void onError(
+                    @NonNull OnDeviceIntelligenceException exception) {
+                try {
+                    featureStatusCallback.onFailure(exception.getErrorCode(),
+                            exception.getMessage(), exception.getErrorParams());
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Error sending feature status: " + e);
+                }
+            }
+        };
+    }
+
+
+    private DownloadCallback wrapDownloadCallback(IDownloadCallback downloadCallback) {
+        return new DownloadCallback() {
+            @Override
+            public void onDownloadStarted(long bytesToDownload) {
+                try {
+                    downloadCallback.onDownloadStarted(bytesToDownload);
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Error sending download status: " + e);
+                }
+            }
+
+            @Override
+            public void onDownloadFailed(int failureStatus,
+                    String errorMessage, @NonNull PersistableBundle errorParams) {
+                try {
+                    downloadCallback.onDownloadFailed(failureStatus, errorMessage, errorParams);
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Error sending download status: " + e);
+                }
+            }
+
+            @Override
+            public void onDownloadProgress(long totalBytesDownloaded) {
+                try {
+                    downloadCallback.onDownloadProgress(totalBytesDownloaded);
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Error sending download status: " + e);
+                }
+            }
+
+            @Override
+            public void onDownloadCompleted(@NonNull PersistableBundle persistableBundle) {
+                try {
+                    downloadCallback.onDownloadCompleted(persistableBundle);
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Error sending download status: " + e);
+                }
+            }
+        };
+    }
+
+    private static void tryClosePfds(Collection<ParcelFileDescriptor> pfds) {
+        pfds.forEach(pfd -> {
+            try {
+                pfd.close();
+            } catch (Exception e) {
+                Log.w(TAG, "Error closing FD", e);
+            }
+        });
+    }
+
+    private void onGetReadOnlyFileDescriptor(@NonNull String fileName,
+            @NonNull AndroidFuture<ParcelFileDescriptor> future) {
+        Slog.v(TAG, "onGetReadOnlyFileDescriptor " + fileName);
+        Binder.withCleanCallingIdentity(() -> {
+            Slog.v(TAG,
+                    "onGetReadOnlyFileDescriptor: " + fileName + " under internal app storage.");
+            File f = new File(getBaseContext().getFilesDir(), fileName);
+            if (!f.exists()) {
+                f = new File(fileName);
+            }
+            ParcelFileDescriptor pfd = null;
+            try {
+                pfd = ParcelFileDescriptor.open(f, ParcelFileDescriptor.MODE_READ_ONLY);
+                Slog.d(TAG, "Successfully opened a file with ParcelFileDescriptor.");
+            } catch (FileNotFoundException e) {
+                Slog.e(TAG, "Cannot open file. No ParcelFileDescriptor returned.");
+                future.completeExceptionally(e);
+            } finally {
+                future.complete(pfd);
+                if (pfd != null) {
+                    pfd.close();
+                }
+            }
+        });
+    }
+
+    /**
+     * Provide implementation for a scenario when caller wants to get all feature related
+     * file-descriptors that might be required for processing a request for the corresponding the
+     * feature.
+     *
+     * @param feature                   the feature for which files need to be opened.
+     * @param fileDescriptorMapConsumer callback to be populated with a map of file-path and
+     *                                  corresponding ParcelDescriptor to be used in a remote
+     *                                  service.
+     */
+    public abstract void onGetReadOnlyFeatureFileDescriptorMap(
+            @NonNull Feature feature,
+            @NonNull Consumer<Map<String, ParcelFileDescriptor>> fileDescriptorMapConsumer);
+
+    /**
+     * Request download for feature that is requested and listen to download progress updates. If
+     * the download completes successfully, success callback should be populated.
+     *
+     * @param callerUid          UID of the caller that initiated this call chain.
+     * @param feature            the feature for which files need to be downlaoded.
+     *                           process.
+     * @param cancellationSignal signal to attach a listener to, and receive cancellation signals
+     *                           from thw client.
+     * @param downloadCallback   callback to populate download updates for clients to listen on..
+     */
+    public abstract void onDownloadFeature(
+            int callerUid, @NonNull Feature feature,
+            @Nullable CancellationSignal cancellationSignal,
+            @NonNull DownloadCallback downloadCallback);
+
+    /**
+     * Provide feature details for the passed in feature. Usually the client and remote
+     * implementation use the {@link Feature#getFeatureParams()} as a hint to communicate what
+     * details the client is looking for.
+     *
+     * @param callerUid              UID of the caller that initiated this call chain.
+     * @param feature                the feature for which status needs to be known.
+     * @param featureDetailsCallback callback to populate the resulting feature status.
+     */
+    public abstract void onGetFeatureDetails(int callerUid, @NonNull Feature feature,
+            @NonNull OutcomeReceiver<FeatureDetails,
+                    OnDeviceIntelligenceException> featureDetailsCallback);
+
+
+    /**
+     * Get feature using the provided identifier to the remote implementation.
+     *
+     * @param callerUid       UID of the caller that initiated this call chain.
+     * @param featureCallback callback to populate the features list.
+     */
+    public abstract void onGetFeature(int callerUid, int featureId,
+            @NonNull OutcomeReceiver<Feature,
+                    OnDeviceIntelligenceException> featureCallback);
+
+    /**
+     * List all features which are available in the remote implementation. The implementation might
+     * choose to provide only a certain list of features based on the caller.
+     *
+     * @param callerUid            UID of the caller that initiated this call chain.
+     * @param listFeaturesCallback callback to populate the features list.
+     */
+    public abstract void onListFeatures(int callerUid, @NonNull OutcomeReceiver<List<Feature>,
+            OnDeviceIntelligenceException> listFeaturesCallback);
+
+    /**
+     * Provides a long value representing the version of the remote implementation processing
+     * requests.
+     *
+     * @param versionConsumer consumer to populate the version.
+     */
+    public abstract void onGetVersion(@NonNull LongConsumer versionConsumer);
+}
diff --git a/packages/NeuralNetworks/framework/platform/java/android/service/ondeviceintelligence/OnDeviceSandboxedInferenceService.java b/packages/NeuralNetworks/framework/platform/java/android/service/ondeviceintelligence/OnDeviceSandboxedInferenceService.java
new file mode 100644
index 0000000..949fb8d
--- /dev/null
+++ b/packages/NeuralNetworks/framework/platform/java/android/service/ondeviceintelligence/OnDeviceSandboxedInferenceService.java
@@ -0,0 +1,617 @@
+/*
+ * 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 android.service.ondeviceintelligence;
+
+import static android.app.ondeviceintelligence.OnDeviceIntelligenceManager.AUGMENT_REQUEST_CONTENT_BUNDLE_KEY;
+import static android.app.ondeviceintelligence.flags.Flags.FLAG_ENABLE_ON_DEVICE_INTELLIGENCE;
+
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+
+import android.annotation.CallSuper;
+import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SdkConstant;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.app.ondeviceintelligence.Feature;
+import android.app.ondeviceintelligence.IProcessingSignal;
+import android.app.ondeviceintelligence.IResponseCallback;
+import android.app.ondeviceintelligence.IStreamingResponseCallback;
+import android.app.ondeviceintelligence.ITokenInfoCallback;
+import android.app.ondeviceintelligence.OnDeviceIntelligenceException;
+import android.app.ondeviceintelligence.OnDeviceIntelligenceManager;
+import android.app.ondeviceintelligence.OnDeviceIntelligenceManager.InferenceParams;
+import android.app.ondeviceintelligence.OnDeviceIntelligenceManager.StateParams;
+import android.app.ondeviceintelligence.ProcessingCallback;
+import android.app.ondeviceintelligence.ProcessingSignal;
+import android.app.ondeviceintelligence.StreamingProcessingCallback;
+import android.app.ondeviceintelligence.TokenInfo;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.os.Handler;
+import android.os.HandlerExecutor;
+import android.os.IBinder;
+import android.os.ICancellationSignal;
+import android.os.IRemoteCallback;
+import android.os.Looper;
+import android.os.OutcomeReceiver;
+import android.os.ParcelFileDescriptor;
+import android.os.PersistableBundle;
+import android.os.RemoteCallback;
+import android.os.RemoteException;
+import android.util.Log;
+import android.util.Slog;
+
+import com.android.internal.infra.AndroidFuture;
+
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
+
+/**
+ * Abstract base class for performing inference in a isolated process. This service exposes its
+ * methods via {@link OnDeviceIntelligenceManager}.
+ *
+ * <p> A service that provides methods to perform on-device inference both in streaming and
+ * non-streaming fashion. Also, provides a way to register a storage service that will be used to
+ * read-only access files from the {@link OnDeviceIntelligenceService} counterpart. </p>
+ *
+ * <p> Similar to {@link OnDeviceIntelligenceManager} class, the contracts in this service are
+ * defined to be open-ended in general, to allow interoperability. Therefore, it is recommended
+ * that implementations of this system-service expose this API to the clients via a library which
+ * has more defined contract.</p>
+ *
+ * <pre>
+ * {@literal
+ * <service android:name=".SampleSandboxedInferenceService"
+ *          android:permission="android.permission.BIND_ONDEVICE_SANDBOXED_INFERENCE_SERVICE"
+ *          android:isolatedProcess="true">
+ * </service>}
+ * </pre>
+ *
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(FLAG_ENABLE_ON_DEVICE_INTELLIGENCE)
+public abstract class OnDeviceSandboxedInferenceService extends Service {
+    private static final String TAG = OnDeviceSandboxedInferenceService.class.getSimpleName();
+
+    /**
+     * @hide
+     */
+    public static final String INFERENCE_INFO_BUNDLE_KEY = "inference_info";
+
+    /**
+     * 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_ON_DEVICE_SANDBOXED_INFERENCE_SERVICE}
+     * permission so that other applications can not abuse it.
+     */
+    @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
+    public static final String SERVICE_INTERFACE =
+            "android.service.ondeviceintelligence.OnDeviceSandboxedInferenceService";
+
+    // TODO(339594686): make API
+    /**
+     * @hide
+     */
+    public static final String REGISTER_MODEL_UPDATE_CALLBACK_BUNDLE_KEY =
+            "register_model_update_callback";
+    /**
+     * @hide
+     */
+    public static final String MODEL_LOADED_BUNDLE_KEY = "model_loaded";
+    /**
+     * @hide
+     */
+    public static final String MODEL_UNLOADED_BUNDLE_KEY = "model_unloaded";
+    /**
+     * @hide
+     */
+    public static final String MODEL_LOADED_BROADCAST_INTENT =
+        "android.service.ondeviceintelligence.MODEL_LOADED";
+    /**
+     * @hide
+     */
+    public static final String MODEL_UNLOADED_BROADCAST_INTENT =
+        "android.service.ondeviceintelligence.MODEL_UNLOADED";
+
+    /**
+     * @hide
+     */
+    public static final String DEVICE_CONFIG_UPDATE_BUNDLE_KEY = "device_config_update";
+
+    private IRemoteStorageService mRemoteStorageService;
+    private Handler mHandler;
+
+    @CallSuper
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        mHandler = new Handler(Looper.getMainLooper(), null /* callback */, true /* async */);
+    }
+
+    /**
+     * @hide
+     */
+    @Nullable
+    @Override
+    public final IBinder onBind(@NonNull Intent intent) {
+        if (SERVICE_INTERFACE.equals(intent.getAction())) {
+            return new IOnDeviceSandboxedInferenceService.Stub() {
+                @Override
+                public void registerRemoteStorageService(IRemoteStorageService storageService,
+                        IRemoteCallback remoteCallback) throws RemoteException {
+                    Objects.requireNonNull(storageService);
+                    mRemoteStorageService = storageService;
+                    remoteCallback.sendResult(
+                            Bundle.EMPTY); //to notify caller uid to system-server.
+                }
+
+                @Override
+                public void requestTokenInfo(int callerUid, Feature feature, Bundle request,
+                        AndroidFuture cancellationSignalFuture,
+                        ITokenInfoCallback tokenInfoCallback) {
+                    Objects.requireNonNull(feature);
+                    Objects.requireNonNull(tokenInfoCallback);
+                    ICancellationSignal transport = null;
+                    if (cancellationSignalFuture != null) {
+                        transport = CancellationSignal.createTransport();
+                        cancellationSignalFuture.complete(transport);
+                    }
+
+                    mHandler.executeOrSendMessage(
+                            obtainMessage(
+                                    OnDeviceSandboxedInferenceService::onTokenInfoRequest,
+                                    OnDeviceSandboxedInferenceService.this,
+                                    callerUid, feature,
+                                    request,
+                                    CancellationSignal.fromTransport(transport),
+                                    wrapTokenInfoCallback(tokenInfoCallback)));
+                }
+
+                @Override
+                public void processRequestStreaming(int callerUid, Feature feature, Bundle request,
+                        int requestType,
+                        AndroidFuture cancellationSignalFuture,
+                        AndroidFuture processingSignalFuture,
+                        IStreamingResponseCallback callback) {
+                    Objects.requireNonNull(feature);
+                    Objects.requireNonNull(callback);
+
+                    ICancellationSignal transport = null;
+                    if (cancellationSignalFuture != null) {
+                        transport = CancellationSignal.createTransport();
+                        cancellationSignalFuture.complete(transport);
+                    }
+                    IProcessingSignal processingSignalTransport = null;
+                    if (processingSignalFuture != null) {
+                        processingSignalTransport = ProcessingSignal.createTransport();
+                        processingSignalFuture.complete(processingSignalTransport);
+                    }
+
+
+                    mHandler.executeOrSendMessage(
+                            obtainMessage(
+                                    OnDeviceSandboxedInferenceService::onProcessRequestStreaming,
+                                    OnDeviceSandboxedInferenceService.this, callerUid,
+                                    feature,
+                                    request,
+                                    requestType,
+                                    CancellationSignal.fromTransport(transport),
+                                    ProcessingSignal.fromTransport(processingSignalTransport),
+                                    wrapStreamingResponseCallback(callback)));
+                }
+
+                @Override
+                public void processRequest(int callerUid, Feature feature, Bundle request,
+                        int requestType,
+                        AndroidFuture cancellationSignalFuture,
+                        AndroidFuture processingSignalFuture,
+                        IResponseCallback callback) {
+                    Objects.requireNonNull(feature);
+                    Objects.requireNonNull(callback);
+                    ICancellationSignal transport = null;
+                    if (cancellationSignalFuture != null) {
+                        transport = CancellationSignal.createTransport();
+                        cancellationSignalFuture.complete(transport);
+                    }
+                    IProcessingSignal processingSignalTransport = null;
+                    if (processingSignalFuture != null) {
+                        processingSignalTransport = ProcessingSignal.createTransport();
+                        processingSignalFuture.complete(processingSignalTransport);
+                    }
+                    mHandler.executeOrSendMessage(
+                            obtainMessage(
+                                    OnDeviceSandboxedInferenceService::onProcessRequest,
+                                    OnDeviceSandboxedInferenceService.this, callerUid, feature,
+                                    request, requestType,
+                                    CancellationSignal.fromTransport(transport),
+                                    ProcessingSignal.fromTransport(processingSignalTransport),
+                                    wrapResponseCallback(callback)));
+                }
+
+                @Override
+                public void updateProcessingState(Bundle processingState,
+                        IProcessingUpdateStatusCallback callback) {
+                    Objects.requireNonNull(processingState);
+                    Objects.requireNonNull(callback);
+                    mHandler.executeOrSendMessage(
+                            obtainMessage(
+                                    OnDeviceSandboxedInferenceService::onUpdateProcessingState,
+                                    OnDeviceSandboxedInferenceService.this, processingState,
+                                    wrapOutcomeReceiver(callback)));
+                }
+            };
+        }
+        Slog.w(TAG, "Incorrect service interface, returning null.");
+        return null;
+    }
+
+    /**
+     * Invoked when caller  wants to obtain token info related to the payload in the passed
+     * content, associated with the provided feature.
+     * The expectation from the implementation is that when processing is complete, it
+     * should provide the token info in the {@link OutcomeReceiver#onResult}.
+     *
+     * @param callerUid          UID of the caller that initiated this call chain.
+     * @param feature            feature which is associated with the request.
+     * @param request            request that requires processing.
+     * @param cancellationSignal Cancellation Signal to receive cancellation events from client and
+     *                           configure a listener to.
+     * @param callback           callback to populate failure or the token info for the provided
+     *                           request.
+     */
+    @NonNull
+    public abstract void onTokenInfoRequest(
+            int callerUid, @NonNull Feature feature,
+            @NonNull @InferenceParams Bundle request,
+            @Nullable CancellationSignal cancellationSignal,
+            @NonNull OutcomeReceiver<TokenInfo, OnDeviceIntelligenceException> callback);
+
+    /**
+     * Invoked when caller provides a request for a particular feature to be processed in a
+     * streaming manner. The expectation from the implementation is that when processing the
+     * request,
+     * it periodically populates the {@link StreamingProcessingCallback#onPartialResult} to
+     * continuously
+     * provide partial Bundle results for the caller to utilize. Optionally the implementation can
+     * provide the complete response in the {@link StreamingProcessingCallback#onResult} upon
+     * processing completion.
+     *
+     * @param callerUid          UID of the caller that initiated this call chain.
+     * @param feature            feature which is associated with the request.
+     * @param request            request that requires processing.
+     * @param requestType        identifier representing the type of request.
+     * @param cancellationSignal Cancellation Signal to receive cancellation events from client and
+     *                           configure a listener to.
+     * @param processingSignal   Signal to receive custom action instructions from client.
+     * @param callback           callback to populate the partial responses, failure and optionally
+     *                           full response for the provided request.
+     */
+    @NonNull
+    public abstract void onProcessRequestStreaming(
+            int callerUid, @NonNull Feature feature,
+            @NonNull @InferenceParams Bundle request,
+            @OnDeviceIntelligenceManager.RequestType int requestType,
+            @Nullable CancellationSignal cancellationSignal,
+            @Nullable ProcessingSignal processingSignal,
+            @NonNull StreamingProcessingCallback callback);
+
+    /**
+     * Invoked when caller provides a request for a particular feature to be processed in one shot
+     * completely.
+     * The expectation from the implementation is that when processing the request is complete, it
+     * should
+     * provide the complete response in the {@link OutcomeReceiver#onResult}.
+     *
+     * @param callerUid          UID of the caller that initiated this call chain.
+     * @param feature            feature which is associated with the request.
+     * @param request            request that requires processing.
+     * @param requestType        identifier representing the type of request.
+     * @param cancellationSignal Cancellation Signal to receive cancellation events from client and
+     *                           configure a listener to.
+     * @param processingSignal   Signal to receive custom action instructions from client.
+     * @param callback           callback to populate failure and full response for the provided
+     *                           request.
+     */
+    @NonNull
+    public abstract void onProcessRequest(
+            int callerUid, @NonNull Feature feature,
+            @NonNull @InferenceParams Bundle request,
+            @OnDeviceIntelligenceManager.RequestType int requestType,
+            @Nullable CancellationSignal cancellationSignal,
+            @Nullable ProcessingSignal processingSignal,
+            @NonNull ProcessingCallback callback);
+
+
+    /**
+     * Invoked when processing environment needs to be updated or refreshed with fresh
+     * configuration, files or state.
+     *
+     * @param processingState contains updated state and params that are to be applied to the
+     *                        processing environmment,
+     * @param callback        callback to populate the update status and if there are params
+     *                        associated with the status.
+     */
+    public abstract void onUpdateProcessingState(@NonNull @StateParams Bundle processingState,
+            @NonNull OutcomeReceiver<PersistableBundle,
+                    OnDeviceIntelligenceException> callback);
+
+
+    /**
+     * Overrides {@link Context#openFileInput} to read files with the given file names under the
+     * internal app storage of the {@link OnDeviceIntelligenceService}, i.e., only files stored in
+     * {@link Context#getFilesDir()} can be opened.
+     */
+    @Override
+    public final FileInputStream openFileInput(@NonNull String filename) throws
+            FileNotFoundException {
+        try {
+            AndroidFuture<ParcelFileDescriptor> future = new AndroidFuture<>();
+            mRemoteStorageService.getReadOnlyFileDescriptor(filename, future);
+            ParcelFileDescriptor pfd = future.get();
+            return new FileInputStream(pfd.getFileDescriptor());
+        } catch (RemoteException | ExecutionException | InterruptedException e) {
+            Log.w(TAG, "Cannot open file due to remote service failure");
+            throw new FileNotFoundException(e.getMessage());
+        }
+    }
+
+    /**
+     * Provides read-only access to the internal app storage via the
+     * {@link OnDeviceIntelligenceService}. This is an asynchronous alternative for
+     * {@link #openFileInput(String)}.
+     *
+     * @param fileName       File name relative to the {@link Context#getFilesDir()}.
+     * @param resultConsumer Consumer to populate the corresponding file descriptor in.
+     */
+    public final void getReadOnlyFileDescriptor(@NonNull String fileName,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull Consumer<ParcelFileDescriptor> resultConsumer) throws FileNotFoundException {
+        AndroidFuture<ParcelFileDescriptor> future = new AndroidFuture<>();
+        try {
+            mRemoteStorageService.getReadOnlyFileDescriptor(fileName, future);
+        } catch (RemoteException e) {
+            Log.w(TAG, "Cannot open file due to remote service failure");
+            throw new FileNotFoundException(e.getMessage());
+        }
+        future.whenCompleteAsync((pfd, err) -> {
+            if (err != null) {
+                Log.e(TAG, "Failure when reading file: " + fileName + err);
+                executor.execute(() -> resultConsumer.accept(null));
+            } else {
+                executor.execute(
+                        () -> resultConsumer.accept(pfd));
+            }
+        }, executor);
+    }
+
+    /**
+     * Provides access to all file streams required for feature via the
+     * {@link OnDeviceIntelligenceService}.
+     *
+     * @param feature        Feature for which the associated files should be fetched.
+     * @param executor       Executor to run the consumer callback on.
+     * @param resultConsumer Consumer to receive a map of filePath to the corresponding file input
+     *                       stream.
+     */
+    public final void fetchFeatureFileDescriptorMap(@NonNull Feature feature,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull Consumer<Map<String, ParcelFileDescriptor>> resultConsumer) {
+        try {
+            mRemoteStorageService.getReadOnlyFeatureFileDescriptorMap(feature,
+                    wrapAsRemoteCallback(resultConsumer, executor));
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+
+    /**
+     * Returns the {@link Executor} to use for incoming IPC from request sender into your service
+     * implementation. For e.g. see
+     * {@link ProcessingCallback#onDataAugmentRequest(Bundle,
+     * Consumer)} where we use the executor to populate the consumer.
+     * <p>
+     * Override this method in your {@link OnDeviceSandboxedInferenceService} implementation to
+     * provide the executor you want to use for incoming IPC.
+     *
+     * @return the {@link Executor} to use for incoming IPC from {@link OnDeviceIntelligenceManager}
+     * to {@link OnDeviceSandboxedInferenceService}.
+     */
+    @SuppressLint("OnNameExpected")
+    @NonNull
+    public Executor getCallbackExecutor() {
+        return new HandlerExecutor(Handler.createAsync(getMainLooper()));
+    }
+
+
+    private RemoteCallback wrapAsRemoteCallback(
+            @NonNull Consumer<Map<String, ParcelFileDescriptor>> resultConsumer,
+            @NonNull Executor executor) {
+        return new RemoteCallback(result -> {
+            if (result == null) {
+                executor.execute(() -> resultConsumer.accept(new HashMap<>()));
+            } else {
+                Map<String, ParcelFileDescriptor> pfdMap = new HashMap<>();
+                result.keySet().forEach(key ->
+                        pfdMap.put(key, result.getParcelable(key,
+                                ParcelFileDescriptor.class)));
+                executor.execute(() -> resultConsumer.accept(pfdMap));
+            }
+        });
+    }
+
+    private ProcessingCallback wrapResponseCallback(
+            IResponseCallback callback) {
+        return new ProcessingCallback() {
+            @Override
+            public void onResult(@NonNull Bundle result) {
+                try {
+                    callback.onSuccess(result);
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Error sending result: " + e);
+                }
+            }
+
+            @Override
+            public void onError(
+                    OnDeviceIntelligenceException exception) {
+                try {
+                    callback.onFailure(exception.getErrorCode(), exception.getMessage(),
+                            exception.getErrorParams());
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Error sending result: " + e);
+                }
+            }
+
+            @Override
+            public void onDataAugmentRequest(@NonNull Bundle content,
+                    @NonNull Consumer<Bundle> contentCallback) {
+                try {
+                    callback.onDataAugmentRequest(content, wrapRemoteCallback(contentCallback));
+
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Error sending augment request: " + e);
+                }
+            }
+        };
+    }
+
+    private StreamingProcessingCallback wrapStreamingResponseCallback(
+            IStreamingResponseCallback callback) {
+        return new StreamingProcessingCallback() {
+            @Override
+            public void onPartialResult(@NonNull Bundle partialResult) {
+                try {
+                    callback.onNewContent(partialResult);
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Error sending result: " + e);
+                }
+            }
+
+            @Override
+            public void onResult(@NonNull Bundle result) {
+                try {
+                    callback.onSuccess(result);
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Error sending result: " + e);
+                }
+            }
+
+            @Override
+            public void onError(
+                    OnDeviceIntelligenceException exception) {
+                try {
+                    callback.onFailure(exception.getErrorCode(), exception.getMessage(),
+                            exception.getErrorParams());
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Error sending result: " + e);
+                }
+            }
+
+            @Override
+            public void onDataAugmentRequest(@NonNull Bundle content,
+                    @NonNull Consumer<Bundle> contentCallback) {
+                try {
+                    callback.onDataAugmentRequest(content, wrapRemoteCallback(contentCallback));
+
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Error sending augment request: " + e);
+                }
+            }
+        };
+    }
+
+    private RemoteCallback wrapRemoteCallback(
+            @NonNull Consumer<Bundle> contentCallback) {
+        return new RemoteCallback(
+                result -> {
+                    if (result != null) {
+                        getCallbackExecutor().execute(() -> contentCallback.accept(
+                                result.getParcelable(AUGMENT_REQUEST_CONTENT_BUNDLE_KEY,
+                                        Bundle.class)));
+                    } else {
+                        getCallbackExecutor().execute(
+                                () -> contentCallback.accept(null));
+                    }
+                });
+    }
+
+    private OutcomeReceiver<TokenInfo, OnDeviceIntelligenceException> wrapTokenInfoCallback(
+            ITokenInfoCallback tokenInfoCallback) {
+        return new OutcomeReceiver<>() {
+            @Override
+            public void onResult(TokenInfo tokenInfo) {
+                try {
+                    tokenInfoCallback.onSuccess(tokenInfo);
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Error sending result: " + e);
+                }
+            }
+
+            @Override
+            public void onError(
+                    OnDeviceIntelligenceException exception) {
+                try {
+                    tokenInfoCallback.onFailure(exception.getErrorCode(), exception.getMessage(),
+                            exception.getErrorParams());
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Error sending failure: " + e);
+                }
+            }
+        };
+    }
+
+    @NonNull
+    private static OutcomeReceiver<PersistableBundle, OnDeviceIntelligenceException> wrapOutcomeReceiver(
+            IProcessingUpdateStatusCallback callback) {
+        return new OutcomeReceiver<>() {
+            @Override
+            public void onResult(@NonNull PersistableBundle result) {
+                try {
+                    callback.onSuccess(result);
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Error sending result: " + e);
+
+                }
+            }
+
+            @Override
+            public void onError(
+                    @NonNull OnDeviceIntelligenceException error) {
+                try {
+                    callback.onFailure(error.getErrorCode(), error.getMessage());
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Error sending exception details: " + e);
+                }
+            }
+        };
+    }
+
+}
diff --git a/packages/NeuralNetworks/service/Android.bp b/packages/NeuralNetworks/service/Android.bp
index 05c603f..cfdc1af 100644
--- a/packages/NeuralNetworks/service/Android.bp
+++ b/packages/NeuralNetworks/service/Android.bp
@@ -19,11 +19,20 @@
 filegroup {
     name: "service-ondeviceintelligence-sources",
     srcs: [
-        "java/**/*.java",
+        "module/java/**/*.java",
     ],
-    path: "java",
     visibility: [
         "//frameworks/base:__subpackages__",
         "//packages/modules/NeuralNetworks:__subpackages__",
     ],
 }
+
+filegroup {
+    name: "service-ondeviceintelligence-sources-platform",
+    srcs: [
+        "platform/java/**/*.java",
+    ],
+    visibility: [
+        "//frameworks/base:__subpackages__",
+    ],
+}
diff --git a/packages/NeuralNetworks/service/java/com/android/server/ondeviceintelligence/BundleUtil.java b/packages/NeuralNetworks/service/module/java/com/android/server/ondeviceintelligence/BundleUtil.java
similarity index 100%
rename from packages/NeuralNetworks/service/java/com/android/server/ondeviceintelligence/BundleUtil.java
rename to packages/NeuralNetworks/service/module/java/com/android/server/ondeviceintelligence/BundleUtil.java
diff --git a/packages/NeuralNetworks/service/java/com/android/server/ondeviceintelligence/InferenceInfoStore.java b/packages/NeuralNetworks/service/module/java/com/android/server/ondeviceintelligence/InferenceInfoStore.java
similarity index 100%
rename from packages/NeuralNetworks/service/java/com/android/server/ondeviceintelligence/InferenceInfoStore.java
rename to packages/NeuralNetworks/service/module/java/com/android/server/ondeviceintelligence/InferenceInfoStore.java
diff --git a/packages/NeuralNetworks/service/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerLocal.java b/packages/NeuralNetworks/service/module/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerLocal.java
similarity index 100%
rename from packages/NeuralNetworks/service/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerLocal.java
rename to packages/NeuralNetworks/service/module/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerLocal.java
diff --git a/packages/NeuralNetworks/service/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java b/packages/NeuralNetworks/service/module/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java
similarity index 97%
rename from packages/NeuralNetworks/service/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java
rename to packages/NeuralNetworks/service/module/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java
index 9ae0f03..a078f75 100644
--- a/packages/NeuralNetworks/service/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java
+++ b/packages/NeuralNetworks/service/module/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java
@@ -760,13 +760,8 @@
             if (mTemporaryConfigNamespace != null) {
                 return mTemporaryConfigNamespace;
             }
-            return mContext.getResources()
-                    .getString(
-                            mContext.getResources()
-                                    .getIdentifier(
-                                            "config_defaultOnDeviceIntelligenceDeviceConfigNamespace",
-                                            "string",
-                                            "android"));
+            return mContext.getResources().getString(
+                    android.R.string.config_defaultOnDeviceIntelligenceDeviceConfigNamespace);
         }
     }
 
@@ -948,22 +943,10 @@
                 return mTemporaryServiceNames;
             }
         }
-        return new String[]{
-                mContext.getResources()
-                        .getString(
-                        mContext.getResources()
-                                .getIdentifier(
-                                        "config_defaultOnDeviceIntelligenceService",
-                                        "string",
-                                        "android")),
-                mContext.getResources()
-                        .getString(
-                        mContext.getResources()
-                                .getIdentifier(
-                                        "config_defaultOnDeviceSandboxedInferenceService",
-                                        "string",
-                                        "android"))
-        };
+        return new String[]{mContext.getResources().getString(
+                android.R.string.config_defaultOnDeviceIntelligenceService),
+                mContext.getResources().getString(
+                        android.R.string.config_defaultOnDeviceSandboxedInferenceService)};
     }
 
     protected String[] getBroadcastKeys() throws Resources.NotFoundException {
diff --git a/packages/NeuralNetworks/service/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceShellCommand.java b/packages/NeuralNetworks/service/module/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceShellCommand.java
similarity index 100%
rename from packages/NeuralNetworks/service/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceShellCommand.java
rename to packages/NeuralNetworks/service/module/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceShellCommand.java
diff --git a/packages/NeuralNetworks/service/java/com/android/server/ondeviceintelligence/RemoteOnDeviceIntelligenceService.java b/packages/NeuralNetworks/service/module/java/com/android/server/ondeviceintelligence/RemoteOnDeviceIntelligenceService.java
similarity index 100%
rename from packages/NeuralNetworks/service/java/com/android/server/ondeviceintelligence/RemoteOnDeviceIntelligenceService.java
rename to packages/NeuralNetworks/service/module/java/com/android/server/ondeviceintelligence/RemoteOnDeviceIntelligenceService.java
diff --git a/packages/NeuralNetworks/service/java/com/android/server/ondeviceintelligence/RemoteOnDeviceSandboxedInferenceService.java b/packages/NeuralNetworks/service/module/java/com/android/server/ondeviceintelligence/RemoteOnDeviceSandboxedInferenceService.java
similarity index 100%
rename from packages/NeuralNetworks/service/java/com/android/server/ondeviceintelligence/RemoteOnDeviceSandboxedInferenceService.java
rename to packages/NeuralNetworks/service/module/java/com/android/server/ondeviceintelligence/RemoteOnDeviceSandboxedInferenceService.java
diff --git a/packages/NeuralNetworks/service/java/com/android/server/ondeviceintelligence/callbacks/ListenableDownloadCallback.java b/packages/NeuralNetworks/service/module/java/com/android/server/ondeviceintelligence/callbacks/ListenableDownloadCallback.java
similarity index 100%
rename from packages/NeuralNetworks/service/java/com/android/server/ondeviceintelligence/callbacks/ListenableDownloadCallback.java
rename to packages/NeuralNetworks/service/module/java/com/android/server/ondeviceintelligence/callbacks/ListenableDownloadCallback.java
diff --git a/packages/NeuralNetworks/service/platform/java/com/android/server/ondeviceintelligence/BundleUtil.java b/packages/NeuralNetworks/service/platform/java/com/android/server/ondeviceintelligence/BundleUtil.java
new file mode 100644
index 0000000..7dd8f2f
--- /dev/null
+++ b/packages/NeuralNetworks/service/platform/java/com/android/server/ondeviceintelligence/BundleUtil.java
@@ -0,0 +1,407 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.ondeviceintelligence;
+
+import static android.system.OsConstants.F_GETFL;
+import static android.system.OsConstants.O_ACCMODE;
+import static android.system.OsConstants.O_RDONLY;
+import static android.system.OsConstants.PROT_READ;
+
+import android.app.ondeviceintelligence.IResponseCallback;
+import android.app.ondeviceintelligence.IStreamingResponseCallback;
+import android.app.ondeviceintelligence.ITokenInfoCallback;
+import android.app.ondeviceintelligence.OnDeviceIntelligenceManager.InferenceParams;
+import android.app.ondeviceintelligence.OnDeviceIntelligenceManager.ResponseParams;
+import android.app.ondeviceintelligence.OnDeviceIntelligenceManager.StateParams;
+import android.app.ondeviceintelligence.TokenInfo;
+import android.database.CursorWindow;
+import android.graphics.Bitmap;
+import android.os.BadParcelableException;
+import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
+import android.os.Parcelable;
+import android.os.PersistableBundle;
+import android.os.RemoteCallback;
+import android.os.RemoteException;
+import android.os.SharedMemory;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.util.Log;
+
+import com.android.internal.infra.AndroidFuture;
+
+import java.util.concurrent.Executor;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * Util methods for ensuring the Bundle passed in various methods are read-only and restricted to
+ * some known types.
+ */
+public class BundleUtil {
+    private static final String TAG = "BundleUtil";
+
+    /**
+     * Validation of the inference request payload as described in {@link InferenceParams}
+     * description.
+     *
+     * @throws BadParcelableException when the bundle does not meet the read-only requirements.
+     */
+    public static void sanitizeInferenceParams(
+            @InferenceParams Bundle bundle) {
+        ensureValidBundle(bundle);
+
+        if (!bundle.hasFileDescriptors()) {
+            return; //safe to exit if there are no FDs and Binders
+        }
+
+        for (String key : bundle.keySet()) {
+            Object obj = bundle.get(key);
+            if (obj == null) {
+                /* Null value here could also mean deserializing a custom parcelable has failed,
+                 *  and since {@link Bundle} is marked as defusable in system-server - the
+                 * {@link ClassNotFoundException} exception is swallowed and `null` is returned
+                 * instead. We want to ensure cleanup of null entries in such case.
+                 */
+                bundle.putObject(key, null);
+                continue;
+            }
+            if (canMarshall(obj) || obj instanceof CursorWindow) {
+                continue;
+            }
+            if (obj instanceof Bundle) {
+              sanitizeInferenceParams((Bundle) obj);
+            } else if (obj instanceof ParcelFileDescriptor) {
+                validatePfdReadOnly((ParcelFileDescriptor) obj);
+            } else if (obj instanceof SharedMemory) {
+                ((SharedMemory) obj).setProtect(PROT_READ);
+            } else if (obj instanceof Bitmap) {
+                validateBitmap((Bitmap) obj);
+            } else if (obj instanceof Parcelable[]) {
+                validateParcelableArray((Parcelable[]) obj);
+            } else {
+                throw new BadParcelableException(
+                        "Unsupported Parcelable type encountered in the Bundle: "
+                                + obj.getClass().getSimpleName());
+            }
+        }
+    }
+
+    /**
+     * Validation of the inference request payload as described in {@link ResponseParams}
+     * description.
+     *
+     * @throws BadParcelableException when the bundle does not meet the read-only requirements.
+     */
+    public static void sanitizeResponseParams(
+            @ResponseParams Bundle bundle) {
+        ensureValidBundle(bundle);
+
+        if (!bundle.hasFileDescriptors()) {
+            return; //safe to exit if there are no FDs and Binders
+        }
+
+        for (String key : bundle.keySet()) {
+            Object obj = bundle.get(key);
+            if (obj == null) {
+                /* Null value here could also mean deserializing a custom parcelable has failed,
+                 *  and since {@link Bundle} is marked as defusable in system-server - the
+                 * {@link ClassNotFoundException} exception is swallowed and `null` is returned
+                 * instead. We want to ensure cleanup of null entries in such case.
+                 */
+                bundle.putObject(key, null);
+                continue;
+            }
+            if (canMarshall(obj)) {
+                continue;
+            }
+
+            if (obj instanceof Bundle) {
+                sanitizeResponseParams((Bundle) obj);
+            } else if (obj instanceof ParcelFileDescriptor) {
+                validatePfdReadOnly((ParcelFileDescriptor) obj);
+            } else if (obj instanceof Bitmap) {
+                validateBitmap((Bitmap) obj);
+            } else if (obj instanceof Parcelable[]) {
+                validateParcelableArray((Parcelable[]) obj);
+            } else {
+                throw new BadParcelableException(
+                        "Unsupported Parcelable type encountered in the Bundle: "
+                                + obj.getClass().getSimpleName());
+            }
+        }
+    }
+
+    /**
+     * Validation of the inference request payload as described in {@link StateParams}
+     * description.
+     *
+     * @throws BadParcelableException when the bundle does not meet the read-only requirements.
+     */
+    public static void sanitizeStateParams(
+            @StateParams Bundle bundle) {
+        ensureValidBundle(bundle);
+
+        if (!bundle.hasFileDescriptors()) {
+            return; //safe to exit if there are no FDs and Binders
+        }
+
+        for (String key : bundle.keySet()) {
+            Object obj = bundle.get(key);
+            if (obj == null) {
+                /* Null value here could also mean deserializing a custom parcelable has failed,
+                 *  and since {@link Bundle} is marked as defusable in system-server - the
+                 * {@link ClassNotFoundException} exception is swallowed and `null` is returned
+                 * instead. We want to ensure cleanup of null entries in such case.
+                 */
+                bundle.putObject(key, null);
+                continue;
+            }
+            if (canMarshall(obj)) {
+                continue;
+            }
+
+            if (obj instanceof ParcelFileDescriptor) {
+                validatePfdReadOnly((ParcelFileDescriptor) obj);
+            } else {
+                throw new BadParcelableException(
+                        "Unsupported Parcelable type encountered in the Bundle: "
+                                + obj.getClass().getSimpleName());
+            }
+        }
+    }
+
+
+    public static IStreamingResponseCallback wrapWithValidation(
+            IStreamingResponseCallback streamingResponseCallback,
+            Executor resourceClosingExecutor,
+            AndroidFuture future,
+            InferenceInfoStore inferenceInfoStore) {
+        return new IStreamingResponseCallback.Stub() {
+            @Override
+            public void onNewContent(Bundle processedResult) throws RemoteException {
+                try {
+                    sanitizeResponseParams(processedResult);
+                    streamingResponseCallback.onNewContent(processedResult);
+                } finally {
+                    resourceClosingExecutor.execute(() -> tryCloseResource(processedResult));
+                }
+            }
+
+            @Override
+            public void onSuccess(Bundle resultBundle)
+                    throws RemoteException {
+                try {
+                    sanitizeResponseParams(resultBundle);
+                    streamingResponseCallback.onSuccess(resultBundle);
+                } finally {
+                    inferenceInfoStore.addInferenceInfoFromBundle(resultBundle);
+                    resourceClosingExecutor.execute(() -> tryCloseResource(resultBundle));
+                    future.complete(null);
+                }
+            }
+
+            @Override
+            public void onFailure(int errorCode, String errorMessage,
+                    PersistableBundle errorParams) throws RemoteException {
+                streamingResponseCallback.onFailure(errorCode, errorMessage, errorParams);
+                inferenceInfoStore.addInferenceInfoFromBundle(errorParams);
+                future.completeExceptionally(new TimeoutException());
+            }
+
+            @Override
+            public void onDataAugmentRequest(Bundle processedContent,
+                    RemoteCallback remoteCallback)
+                    throws RemoteException {
+                try {
+                    sanitizeResponseParams(processedContent);
+                    streamingResponseCallback.onDataAugmentRequest(processedContent,
+                            new RemoteCallback(
+                                    augmentedData -> {
+                                        try {
+                                            sanitizeInferenceParams(augmentedData);
+                                            remoteCallback.sendResult(augmentedData);
+                                        } finally {
+                                            resourceClosingExecutor.execute(
+                                                    () -> tryCloseResource(augmentedData));
+                                        }
+                                    }));
+                } finally {
+                    resourceClosingExecutor.execute(() -> tryCloseResource(processedContent));
+                }
+            }
+        };
+    }
+
+    public static IResponseCallback wrapWithValidation(IResponseCallback responseCallback,
+            Executor resourceClosingExecutor,
+            AndroidFuture future,
+            InferenceInfoStore inferenceInfoStore) {
+        return new IResponseCallback.Stub() {
+            @Override
+            public void onSuccess(Bundle resultBundle)
+                    throws RemoteException {
+                try {
+                    sanitizeResponseParams(resultBundle);
+                    responseCallback.onSuccess(resultBundle);
+                } finally {
+                    inferenceInfoStore.addInferenceInfoFromBundle(resultBundle);
+                    resourceClosingExecutor.execute(() -> tryCloseResource(resultBundle));
+                    future.complete(null);
+                }
+            }
+
+            @Override
+            public void onFailure(int errorCode, String errorMessage,
+                    PersistableBundle errorParams) throws RemoteException {
+                responseCallback.onFailure(errorCode, errorMessage, errorParams);
+                inferenceInfoStore.addInferenceInfoFromBundle(errorParams);
+                future.completeExceptionally(new TimeoutException());
+            }
+
+            @Override
+            public void onDataAugmentRequest(Bundle processedContent,
+                    RemoteCallback remoteCallback)
+                    throws RemoteException {
+                try {
+                    sanitizeResponseParams(processedContent);
+                    responseCallback.onDataAugmentRequest(processedContent, new RemoteCallback(
+                            augmentedData -> {
+                                try {
+                                    sanitizeInferenceParams(augmentedData);
+                                    remoteCallback.sendResult(augmentedData);
+                                } finally {
+                                    resourceClosingExecutor.execute(
+                                            () -> tryCloseResource(augmentedData));
+                                }
+                            }));
+                } finally {
+                    resourceClosingExecutor.execute(() -> tryCloseResource(processedContent));
+                }
+            }
+        };
+    }
+
+
+    public static ITokenInfoCallback wrapWithValidation(ITokenInfoCallback responseCallback,
+            AndroidFuture future,
+            InferenceInfoStore inferenceInfoStore) {
+        return new ITokenInfoCallback.Stub() {
+            @Override
+            public void onSuccess(TokenInfo tokenInfo) throws RemoteException {
+                responseCallback.onSuccess(tokenInfo);
+                inferenceInfoStore.addInferenceInfoFromBundle(tokenInfo.getInfoParams());
+                future.complete(null);
+            }
+
+            @Override
+            public void onFailure(int errorCode, String errorMessage, PersistableBundle errorParams)
+                    throws RemoteException {
+                responseCallback.onFailure(errorCode, errorMessage, errorParams);
+                inferenceInfoStore.addInferenceInfoFromBundle(errorParams);
+                future.completeExceptionally(new TimeoutException());
+            }
+        };
+    }
+
+    private static boolean canMarshall(Object obj) {
+        return obj instanceof byte[] || obj instanceof PersistableBundle
+                || PersistableBundle.isValidType(obj);
+    }
+
+    private static void ensureValidBundle(Bundle bundle) {
+        if (bundle == null) {
+            throw new IllegalArgumentException("Request passed is expected to be non-null");
+        }
+
+        if (bundle.hasBinders() != Bundle.STATUS_BINDERS_NOT_PRESENT) {
+            throw new BadParcelableException("Bundle should not contain IBinder objects.");
+        }
+    }
+
+    private static void validateParcelableArray(Parcelable[] parcelables) {
+        if (parcelables.length > 0
+                && parcelables[0] instanceof ParcelFileDescriptor) {
+            // Safe to cast
+            validatePfdsReadOnly(parcelables);
+        } else if (parcelables.length > 0
+                && parcelables[0] instanceof Bitmap) {
+            validateBitmapsImmutable(parcelables);
+        } else {
+            throw new BadParcelableException(
+                    "Could not cast to any known parcelable array");
+        }
+    }
+
+    public static void validatePfdsReadOnly(Parcelable[] pfds) {
+        for (Parcelable pfd : pfds) {
+            validatePfdReadOnly((ParcelFileDescriptor) pfd);
+        }
+    }
+
+    public static void validatePfdReadOnly(ParcelFileDescriptor pfd) {
+        if (pfd == null) {
+            return;
+        }
+        try {
+            int readMode = Os.fcntlInt(pfd.getFileDescriptor(), F_GETFL, 0) & O_ACCMODE;
+            if (readMode != O_RDONLY) {
+                throw new BadParcelableException(
+                        "Bundle contains a parcel file descriptor which is not read-only.");
+            }
+        } catch (ErrnoException e) {
+            throw new BadParcelableException(
+                    "Invalid File descriptor passed in the Bundle.", e);
+        }
+    }
+
+    private static void validateBitmap(Bitmap obj) {
+        if (obj.isMutable()) {
+            throw new BadParcelableException(
+                    "Encountered a mutable Bitmap in the Bundle at key : " + obj);
+        }
+    }
+
+    private static void validateBitmapsImmutable(Parcelable[] bitmaps) {
+        for (Parcelable bitmap : bitmaps) {
+            validateBitmap((Bitmap) bitmap);
+        }
+    }
+
+    public static void tryCloseResource(Bundle bundle) {
+        if (bundle == null || bundle.isEmpty() || !bundle.hasFileDescriptors()) {
+            return;
+        }
+
+        for (String key : bundle.keySet()) {
+            Object obj = bundle.get(key);
+
+            try {
+                // TODO(b/329898589) : This can be cleaned up after the flag passing is fixed.
+                if (obj instanceof ParcelFileDescriptor) {
+                    ((ParcelFileDescriptor) obj).close();
+                } else if (obj instanceof CursorWindow) {
+                    ((CursorWindow) obj).close();
+                } else if (obj instanceof SharedMemory) {
+                    // TODO(b/331796886) : Shared memory should honour parcelable flags.
+                    ((SharedMemory) obj).close();
+                }
+            } catch (Exception e) {
+                Log.e(TAG, "Error closing resource with key: " + key, e);
+            }
+        }
+    }
+}
diff --git a/packages/NeuralNetworks/service/platform/java/com/android/server/ondeviceintelligence/InferenceInfoStore.java b/packages/NeuralNetworks/service/platform/java/com/android/server/ondeviceintelligence/InferenceInfoStore.java
new file mode 100644
index 0000000..bef3f80
--- /dev/null
+++ b/packages/NeuralNetworks/service/platform/java/com/android/server/ondeviceintelligence/InferenceInfoStore.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.ondeviceintelligence;
+
+import android.app.ondeviceintelligence.InferenceInfo;
+import android.os.Bundle;
+import android.os.PersistableBundle;
+import android.service.ondeviceintelligence.OnDeviceSandboxedInferenceService;
+import android.util.Base64;
+import android.util.Slog;
+
+import java.io.IOException;
+import java.util.Comparator;
+import java.util.List;
+import java.util.TreeSet;
+
+public class InferenceInfoStore {
+    private static final String TAG = "InferenceInfoStore";
+    private final TreeSet<InferenceInfo> inferenceInfos;
+    private final long maxAgeMs;
+
+    public InferenceInfoStore(long maxAgeMs) {
+        this.maxAgeMs = maxAgeMs;
+        this.inferenceInfos = new TreeSet<>(
+                Comparator.comparingLong(InferenceInfo::getStartTimeMillis));
+    }
+
+    public List<InferenceInfo> getLatestInferenceInfo(long startTimeEpochMillis) {
+        return inferenceInfos.stream().filter(
+                info -> info.getStartTimeMillis() > startTimeEpochMillis).toList();
+    }
+
+    public void addInferenceInfoFromBundle(PersistableBundle pb) {
+        if (!pb.containsKey(OnDeviceSandboxedInferenceService.INFERENCE_INFO_BUNDLE_KEY)) {
+            return;
+        }
+
+        try {
+            String infoBytesBase64String = pb.getString(
+                    OnDeviceSandboxedInferenceService.INFERENCE_INFO_BUNDLE_KEY);
+            if (infoBytesBase64String != null) {
+                byte[] infoBytes = Base64.decode(infoBytesBase64String, Base64.DEFAULT);
+                com.android.server.ondeviceintelligence.nano.InferenceInfo inferenceInfo =
+                        com.android.server.ondeviceintelligence.nano.InferenceInfo.parseFrom(
+                                infoBytes);
+                add(inferenceInfo);
+            }
+        } catch (IOException e) {
+            Slog.e(TAG, "Unable to parse InferenceInfo from the received bytes.");
+        }
+    }
+
+    public void addInferenceInfoFromBundle(Bundle b) {
+        if (!b.containsKey(OnDeviceSandboxedInferenceService.INFERENCE_INFO_BUNDLE_KEY)) {
+            return;
+        }
+
+        try {
+            byte[] infoBytes = b.getByteArray(
+                    OnDeviceSandboxedInferenceService.INFERENCE_INFO_BUNDLE_KEY);
+            if (infoBytes != null) {
+                com.android.server.ondeviceintelligence.nano.InferenceInfo inferenceInfo =
+                        com.android.server.ondeviceintelligence.nano.InferenceInfo.parseFrom(
+                                infoBytes);
+                add(inferenceInfo);
+            }
+        } catch (IOException e) {
+            Slog.e(TAG, "Unable to parse InferenceInfo from the received bytes.");
+        }
+    }
+
+    private synchronized void add(com.android.server.ondeviceintelligence.nano.InferenceInfo info) {
+        while (!inferenceInfos.isEmpty()
+                && System.currentTimeMillis() - inferenceInfos.first().getStartTimeMillis()
+                > maxAgeMs) {
+            inferenceInfos.pollFirst();
+        }
+        inferenceInfos.add(toInferenceInfo(info));
+    }
+
+    private static InferenceInfo toInferenceInfo(
+            com.android.server.ondeviceintelligence.nano.InferenceInfo info) {
+        return new InferenceInfo.Builder(info.uid).setStartTimeMillis(
+                info.startTimeMs).setEndTimeMillis(info.endTimeMs).setSuspendedTimeMillis(
+                info.suspendedTimeMs).build();
+    }
+}
\ No newline at end of file
diff --git a/packages/NeuralNetworks/service/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerLocal.java b/packages/NeuralNetworks/service/platform/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerLocal.java
similarity index 100%
copy from packages/NeuralNetworks/service/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerLocal.java
copy to packages/NeuralNetworks/service/platform/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerLocal.java
diff --git a/packages/NeuralNetworks/service/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java b/packages/NeuralNetworks/service/platform/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java
similarity index 85%
copy from packages/NeuralNetworks/service/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java
copy to packages/NeuralNetworks/service/platform/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java
index 9ae0f03..0a69af6 100644
--- a/packages/NeuralNetworks/service/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java
+++ b/packages/NeuralNetworks/service/platform/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java
@@ -16,40 +16,38 @@
 
 package com.android.server.ondeviceintelligence;
 
-import static android.app.ondeviceintelligence.OnDeviceIntelligenceManager.ON_DEVICE_INTELLIGENCE_IDLE_TIMEOUT_MS;
 import static android.service.ondeviceintelligence.OnDeviceSandboxedInferenceService.DEVICE_CONFIG_UPDATE_BUNDLE_KEY;
-import static android.service.ondeviceintelligence.OnDeviceSandboxedInferenceService.MODEL_LOADED_BROADCAST_INTENT;
 import static android.service.ondeviceintelligence.OnDeviceSandboxedInferenceService.MODEL_LOADED_BUNDLE_KEY;
-import static android.service.ondeviceintelligence.OnDeviceSandboxedInferenceService.MODEL_UNLOADED_BROADCAST_INTENT;
 import static android.service.ondeviceintelligence.OnDeviceSandboxedInferenceService.MODEL_UNLOADED_BUNDLE_KEY;
+import static android.service.ondeviceintelligence.OnDeviceSandboxedInferenceService.MODEL_LOADED_BROADCAST_INTENT;
+import static android.service.ondeviceintelligence.OnDeviceSandboxedInferenceService.MODEL_UNLOADED_BROADCAST_INTENT;
 import static android.service.ondeviceintelligence.OnDeviceSandboxedInferenceService.REGISTER_MODEL_UPDATE_CALLBACK_BUNDLE_KEY;
 
 import static com.android.server.ondeviceintelligence.BundleUtil.sanitizeInferenceParams;
-import static com.android.server.ondeviceintelligence.BundleUtil.sanitizeStateParams;
 import static com.android.server.ondeviceintelligence.BundleUtil.validatePfdReadOnly;
+import static com.android.server.ondeviceintelligence.BundleUtil.sanitizeStateParams;
 import static com.android.server.ondeviceintelligence.BundleUtil.wrapWithValidation;
 
+
 import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
+import android.app.AppGlobals;
 import android.app.ondeviceintelligence.DownloadCallback;
 import android.app.ondeviceintelligence.Feature;
 import android.app.ondeviceintelligence.FeatureDetails;
-import android.app.ondeviceintelligence.ICancellationSignal;
 import android.app.ondeviceintelligence.IDownloadCallback;
 import android.app.ondeviceintelligence.IFeatureCallback;
 import android.app.ondeviceintelligence.IFeatureDetailsCallback;
 import android.app.ondeviceintelligence.IListFeaturesCallback;
 import android.app.ondeviceintelligence.IOnDeviceIntelligenceManager;
 import android.app.ondeviceintelligence.IProcessingSignal;
-import android.app.ondeviceintelligence.IRemoteCallback;
 import android.app.ondeviceintelligence.IResponseCallback;
 import android.app.ondeviceintelligence.IStreamingResponseCallback;
 import android.app.ondeviceintelligence.ITokenInfoCallback;
 import android.app.ondeviceintelligence.InferenceInfo;
 import android.app.ondeviceintelligence.OnDeviceIntelligenceException;
-import android.app.ondeviceintelligence.utils.BinderUtils;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -60,12 +58,16 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.ICancellationSignal;
+import android.os.IRemoteCallback;
 import android.os.Looper;
 import android.os.Message;
 import android.os.ParcelFileDescriptor;
 import android.os.PersistableBundle;
 import android.os.RemoteCallback;
 import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.os.ShellCallback;
 import android.os.UserHandle;
 import android.provider.DeviceConfig;
 import android.provider.Settings;
@@ -80,14 +82,17 @@
 import android.util.Log;
 import android.util.Slog;
 
+import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
-import com.android.modules.utils.AndroidFuture;
-import com.android.modules.utils.BackgroundThread;
-import com.android.modules.utils.ServiceConnector;
+import com.android.internal.infra.AndroidFuture;
+import com.android.internal.infra.ServiceConnector;
+import com.android.internal.os.BackgroundThread;
 import com.android.server.LocalManagerRegistry;
 import com.android.server.SystemService;
+import com.android.server.SystemService.TargetUser;
 import com.android.server.ondeviceintelligence.callbacks.ListenableDownloadCallback;
 
+import java.io.FileDescriptor;
 import java.io.IOException;
 import java.util.List;
 import java.util.Objects;
@@ -232,7 +237,7 @@
                     remoteCallback.sendResult(null);
                     return;
                 }
-                ensureRemoteIntelligenceServiceInitialized(/* throwServiceIfInvalid */ true);
+                ensureRemoteIntelligenceServiceInitialized();
                 mRemoteOnDeviceIntelligenceService.postAsync(
                         service -> {
                             AndroidFuture future = new AndroidFuture();
@@ -260,7 +265,7 @@
                             PersistableBundle.EMPTY);
                     return;
                 }
-                ensureRemoteIntelligenceServiceInitialized(/* throwServiceIfInvalid */ true);
+                ensureRemoteIntelligenceServiceInitialized();
                 int callerUid = Binder.getCallingUid();
                 mRemoteOnDeviceIntelligenceService.postAsync(
                         service -> {
@@ -298,7 +303,7 @@
                             PersistableBundle.EMPTY);
                     return;
                 }
-                ensureRemoteIntelligenceServiceInitialized(/* throwServiceIfInvalid */ true);
+                ensureRemoteIntelligenceServiceInitialized();
                 int callerUid = Binder.getCallingUid();
                 mRemoteOnDeviceIntelligenceService.postAsync(
                         service -> {
@@ -342,7 +347,7 @@
                             PersistableBundle.EMPTY);
                     return;
                 }
-                ensureRemoteIntelligenceServiceInitialized(/* throwServiceIfInvalid */ true);
+                ensureRemoteIntelligenceServiceInitialized();
                 int callerUid = Binder.getCallingUid();
                 mRemoteOnDeviceIntelligenceService.postAsync(
                         service -> {
@@ -385,7 +390,7 @@
                             "OnDeviceIntelligenceManagerService is unavailable",
                             PersistableBundle.EMPTY);
                 }
-                ensureRemoteIntelligenceServiceInitialized(/* throwServiceIfInvalid */ true);
+                ensureRemoteIntelligenceServiceInitialized();
                 int callerUid = Binder.getCallingUid();
                 mRemoteOnDeviceIntelligenceService.postAsync(
                         service -> {
@@ -425,7 +430,7 @@
                                 "OnDeviceIntelligenceManagerService is unavailable",
                                 PersistableBundle.EMPTY);
                     }
-                    ensureRemoteInferenceServiceInitialized(/* throwServiceIfInvalid */ true);
+                    ensureRemoteInferenceServiceInitialized();
                     int callerUid = Binder.getCallingUid();
                     result = mRemoteInferenceService.postAsync(
                             service -> {
@@ -469,7 +474,7 @@
                                 "OnDeviceIntelligenceManagerService is unavailable",
                                 PersistableBundle.EMPTY);
                     }
-                    ensureRemoteInferenceServiceInitialized(/* throwServiceIfInvalid */ true);
+                    ensureRemoteInferenceServiceInitialized();
                     int callerUid = Binder.getCallingUid();
                     result = mRemoteInferenceService.postAsync(
                             service -> {
@@ -515,7 +520,7 @@
                                 "OnDeviceIntelligenceManagerService is unavailable",
                                 PersistableBundle.EMPTY);
                     }
-                    ensureRemoteInferenceServiceInitialized(/* throwServiceIfInvalid */ true);
+                    ensureRemoteInferenceServiceInitialized();
                     int callerUid = Binder.getCallingUid();
                     result = mRemoteInferenceService.postAsync(
                             service -> {
@@ -540,31 +545,20 @@
             }
 
             @Override
-            public int handleShellCommand(@NonNull ParcelFileDescriptor in,
-                    @NonNull ParcelFileDescriptor out, @NonNull ParcelFileDescriptor err,
-                    @NonNull String[] args) {
-                return new com.android.server.ondeviceintelligence.OnDeviceIntelligenceShellCommand(
-                        OnDeviceIntelligenceManagerService.this).exec(
-                        this,
-                        in.getFileDescriptor(),
-                        out.getFileDescriptor(),
-                        err.getFileDescriptor(),
-                        args);
+            public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
+                    String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
+                new OnDeviceIntelligenceShellCommand(OnDeviceIntelligenceManagerService.this).exec(
+                        this, in, out, err, args, callback, resultReceiver);
             }
         };
     }
 
-    private boolean ensureRemoteIntelligenceServiceInitialized(boolean throwIfServiceInvalid) {
+    private void ensureRemoteIntelligenceServiceInitialized() {
         synchronized (mLock) {
             if (mRemoteOnDeviceIntelligenceService == null) {
                 String serviceName = getServiceNames()[0];
-                if (!BinderUtils.withCleanCallingIdentity(
-                        () -> validateServiceElevated(serviceName, false,
-                                throwIfServiceInvalid))) {
-                    return false;
-                }
-                mRemoteOnDeviceIntelligenceService = new RemoteOnDeviceIntelligenceService(
-                        mContext,
+                Binder.withCleanCallingIdentity(() -> validateServiceElevated(serviceName, false));
+                mRemoteOnDeviceIntelligenceService = new RemoteOnDeviceIntelligenceService(mContext,
                         ComponentName.unflattenFromString(serviceName),
                         UserHandle.SYSTEM.getIdentifier());
                 mRemoteOnDeviceIntelligenceService.setServiceLifecycleCallbacks(
@@ -583,7 +577,6 @@
                         });
             }
         }
-        return true;
     }
 
     @NonNull
@@ -597,21 +590,13 @@
                     AndroidFuture<Void> result = null;
                     try {
                         sanitizeStateParams(processingState);
-                        if (ensureRemoteInferenceServiceInitialized(/* throwServiceIfInvalid */
-                                false)) {
-                            result = mRemoteInferenceService.post(
-                                    service -> service.updateProcessingState(
-                                            processingState, callback));
-                            result.whenCompleteAsync(
-                                    (c, e) -> BundleUtil.tryCloseResource(processingState),
-                                    resourceClosingExecutor);
-                        } else {
-                            callback.onFailure(
-                                    OnDeviceIntelligenceException.PROCESSING_ERROR_SERVICE_UNAVAILABLE,
-                                    "Remote service cannot be initialized.");
-                        }
-                    } catch (RemoteException e) {
-                        Slog.w("Failed to invoke updateProcessingState", e);
+                        ensureRemoteInferenceServiceInitialized();
+                        result = mRemoteInferenceService.post(
+                                service -> service.updateProcessingState(
+                                        processingState, callback));
+                        result.whenCompleteAsync(
+                                (c, e) -> BundleUtil.tryCloseResource(processingState),
+                                resourceClosingExecutor);
                     } finally {
                         if (result == null) {
                             resourceClosingExecutor.execute(
@@ -623,14 +608,11 @@
         };
     }
 
-    private boolean ensureRemoteInferenceServiceInitialized(boolean throwIfServiceInvalid) {
+    private void ensureRemoteInferenceServiceInitialized() {
         synchronized (mLock) {
             if (mRemoteInferenceService == null) {
                 String serviceName = getServiceNames()[1];
-                if (!BinderUtils.withCleanCallingIdentity(
-                        () -> validateServiceElevated(serviceName, true, throwIfServiceInvalid))) {
-                    return false;
-                }
+                Binder.withCleanCallingIdentity(() -> validateServiceElevated(serviceName, true));
                 mRemoteInferenceService = new RemoteOnDeviceSandboxedInferenceService(mContext,
                         ComponentName.unflattenFromString(serviceName),
                         UserHandle.SYSTEM.getIdentifier());
@@ -640,11 +622,7 @@
                             public void onConnected(
                                     @NonNull IOnDeviceSandboxedInferenceService service) {
                                 try {
-                                    if (!ensureRemoteIntelligenceServiceInitialized(
-                                            /* throwServiceIfInvalid */
-                                            false)) {
-                                        return;
-                                    }
+                                    ensureRemoteIntelligenceServiceInitialized();
                                     service.registerRemoteStorageService(
                                             getIRemoteStorageService(), new IRemoteCallback.Stub() {
                                                 @Override
@@ -667,29 +645,20 @@
                             @Override
                             public void onDisconnected(
                                     @NonNull IOnDeviceSandboxedInferenceService service) {
-                                if (!ensureRemoteIntelligenceServiceInitialized(
-                                        /* throwServiceIfInvalid */
-                                        false)) {
-                                    return;
-                                }
+                                ensureRemoteIntelligenceServiceInitialized();
                                 mRemoteOnDeviceIntelligenceService.run(
                                         IOnDeviceIntelligenceService::notifyInferenceServiceDisconnected);
                             }
 
                             @Override
                             public void onBinderDied() {
-                                if (!ensureRemoteIntelligenceServiceInitialized(
-                                        /* throwServiceIfInvalid */
-                                        false)) {
-                                    return;
-                                }
+                                ensureRemoteIntelligenceServiceInitialized();
                                 mRemoteOnDeviceIntelligenceService.run(
                                         IOnDeviceIntelligenceService::notifyInferenceServiceDisconnected);
                             }
                         });
             }
         }
-        return true;
     }
 
     private void registerModelLoadingBroadcasts(IOnDeviceSandboxedInferenceService service) {
@@ -760,13 +729,9 @@
             if (mTemporaryConfigNamespace != null) {
                 return mTemporaryConfigNamespace;
             }
-            return mContext.getResources()
-                    .getString(
-                            mContext.getResources()
-                                    .getIdentifier(
-                                            "config_defaultOnDeviceIntelligenceDeviceConfigNamespace",
-                                            "string",
-                                            "android"));
+
+            return mContext.getResources().getString(
+                    R.string.config_defaultOnDeviceIntelligenceDeviceConfigNamespace);
         }
     }
 
@@ -780,11 +745,7 @@
         }
         Bundle bundle = new Bundle();
         bundle.putParcelable(DEVICE_CONFIG_UPDATE_BUNDLE_KEY, persistableBundle);
-        if (!ensureRemoteIntelligenceServiceInitialized(
-                /* throwServiceIfInvalid */
-                false)) {
-            return;
-        }
+        ensureRemoteInferenceServiceInitialized();
         mRemoteInferenceService.run(service -> service.updateProcessingState(bundle,
                 new IProcessingUpdateStatusCallback.Stub() {
                     @Override
@@ -807,13 +768,7 @@
             public void getReadOnlyFileDescriptor(
                     String filePath,
                     AndroidFuture<ParcelFileDescriptor> future) {
-                if (!ensureRemoteIntelligenceServiceInitialized(
-                        /* throwServiceIfInvalid */
-                        false)) {
-                    future.completeExceptionally(new OnDeviceIntelligenceException(
-                            OnDeviceIntelligenceException.PROCESSING_ERROR_NOT_AVAILABLE));
-                    return;
-                }
+                ensureRemoteIntelligenceServiceInitialized();
                 AndroidFuture<ParcelFileDescriptor> pfdFuture = new AndroidFuture<>();
                 mRemoteOnDeviceIntelligenceService.run(
                         service -> service.getReadOnlyFileDescriptor(
@@ -836,7 +791,7 @@
             public void getReadOnlyFeatureFileDescriptorMap(
                     Feature feature,
                     RemoteCallback remoteCallback) {
-                ensureRemoteIntelligenceServiceInitialized(/* throwServiceIfInvalid */ true);
+                ensureRemoteIntelligenceServiceInitialized();
                 mRemoteOnDeviceIntelligenceService.run(
                         service -> service.getReadOnlyFeatureFileDescriptorMap(
                                 feature,
@@ -860,48 +815,40 @@
         };
     }
 
-    private boolean validateServiceElevated(String serviceName, boolean checkIsolated,
-            boolean throwIfServiceInvalid) {
+    private void validateServiceElevated(String serviceName, boolean checkIsolated) {
         try {
             if (TextUtils.isEmpty(serviceName)) {
-                if (throwIfServiceInvalid) {
-                    throw new IllegalStateException(
-                            "Remote service is not configured to complete the request");
-                }
-                return false;
+                throw new IllegalStateException(
+                        "Remote service is not configured to complete the request");
             }
             ComponentName serviceComponent = ComponentName.unflattenFromString(
                     serviceName);
-            ServiceInfo serviceInfo = mContext.getPackageManager().getServiceInfo(
+            ServiceInfo serviceInfo = AppGlobals.getPackageManager().getServiceInfo(
                     serviceComponent,
                     PackageManager.MATCH_DIRECT_BOOT_AWARE
-                            | PackageManager.MATCH_DIRECT_BOOT_UNAWARE);
-            if (!checkIsolated) {
-                checkServiceRequiresPermission(serviceInfo,
-                        Manifest.permission.BIND_ON_DEVICE_INTELLIGENCE_SERVICE);
-                return true;
-            }
+                            | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
+                    UserHandle.SYSTEM.getIdentifier());
+            if (serviceInfo != null) {
+                if (!checkIsolated) {
+                    checkServiceRequiresPermission(serviceInfo,
+                            Manifest.permission.BIND_ON_DEVICE_INTELLIGENCE_SERVICE);
+                    return;
+                }
 
-            checkServiceRequiresPermission(serviceInfo,
-                    Manifest.permission.BIND_ON_DEVICE_SANDBOXED_INFERENCE_SERVICE);
-            if (!isIsolatedService(serviceInfo)) {
-                throw new SecurityException(
-                        "Call required an isolated service, but the configured service: "
-                                + serviceName + ", is not isolated");
-            }
-        } catch (PackageManager.NameNotFoundException e) {
-            if (throwIfServiceInvalid) {
+                checkServiceRequiresPermission(serviceInfo,
+                        Manifest.permission.BIND_ON_DEVICE_SANDBOXED_INFERENCE_SERVICE);
+                if (!isIsolatedService(serviceInfo)) {
+                    throw new SecurityException(
+                            "Call required an isolated service, but the configured service: "
+                                    + serviceName + ", is not isolated");
+                }
+            } else {
                 throw new IllegalStateException(
                         "Remote service is not configured to complete the request.");
             }
-            return false;
-        } catch (SecurityException e) {
-            if (throwIfServiceInvalid) {
-                throw e;
-            }
-            return false;
+        } catch (RemoteException e) {
+            throw new IllegalStateException("Could not fetch service info for remote services", e);
         }
-        return true;
     }
 
     private static void checkServiceRequiresPermission(ServiceInfo serviceInfo,
@@ -909,8 +856,8 @@
         final String permission = serviceInfo.permission;
         if (!requiredPermission.equals(permission)) {
             throw new SecurityException(String.format(
-                    "%s requires %s permission. Found %s permission",
-                    serviceInfo,
+                    "Service %s requires %s permission. Found %s permission",
+                    serviceInfo.getComponentName(),
                     requiredPermission,
                     serviceInfo.permission));
         }
@@ -948,22 +895,10 @@
                 return mTemporaryServiceNames;
             }
         }
-        return new String[]{
-                mContext.getResources()
-                        .getString(
-                        mContext.getResources()
-                                .getIdentifier(
-                                        "config_defaultOnDeviceIntelligenceService",
-                                        "string",
-                                        "android")),
-                mContext.getResources()
-                        .getString(
-                        mContext.getResources()
-                                .getIdentifier(
-                                        "config_defaultOnDeviceSandboxedInferenceService",
-                                        "string",
-                                        "android"))
-        };
+        return new String[]{mContext.getResources().getString(
+                R.string.config_defaultOnDeviceIntelligenceService),
+                mContext.getResources().getString(
+                        R.string.config_defaultOnDeviceSandboxedInferenceService)};
     }
 
     protected String[] getBroadcastKeys() throws Resources.NotFoundException {
@@ -974,7 +909,7 @@
             }
         }
 
-        return new String[]{MODEL_LOADED_BROADCAST_INTENT, MODEL_UNLOADED_BROADCAST_INTENT};
+        return new String[]{ MODEL_LOADED_BROADCAST_INTENT, MODEL_UNLOADED_BROADCAST_INTENT };
     }
 
     @RequiresPermission(Manifest.permission.USE_ON_DEVICE_INTELLIGENCE)
@@ -1119,7 +1054,7 @@
 
     private synchronized Handler getTemporaryHandler() {
         if (mTemporaryHandler == null) {
-            mTemporaryHandler = new Handler(Looper.getMainLooper()) {
+            mTemporaryHandler = new Handler(Looper.getMainLooper(), null, true) {
                 @Override
                 public void handleMessage(Message msg) {
                     synchronized (mLock) {
@@ -1141,13 +1076,10 @@
         return mTemporaryHandler;
     }
 
-    // Using #getLong here as the timeout settings are only applicable to the services running in
-    // SYSTEM user only.
-    @SuppressWarnings("NonUserGetterCalled")
     private long getIdleTimeoutMs() {
-        return Settings.Secure.getLong(mContext.getContentResolver(),
-                ON_DEVICE_INTELLIGENCE_IDLE_TIMEOUT_MS,
-                TimeUnit.HOURS.toMillis(1));
+        return Settings.Secure.getLongForUser(mContext.getContentResolver(),
+                Settings.Secure.ON_DEVICE_INTELLIGENCE_IDLE_TIMEOUT_MS, TimeUnit.HOURS.toMillis(1),
+                mContext.getUserId());
     }
 
     private int getRemoteInferenceServiceUid() {
diff --git a/packages/NeuralNetworks/service/platform/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceShellCommand.java b/packages/NeuralNetworks/service/platform/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceShellCommand.java
new file mode 100644
index 0000000..d2c84fa
--- /dev/null
+++ b/packages/NeuralNetworks/service/platform/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceShellCommand.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.ondeviceintelligence;
+
+import android.annotation.NonNull;
+import android.os.Binder;
+import android.os.ShellCommand;
+
+import java.io.PrintWriter;
+import java.util.Objects;
+
+final class OnDeviceIntelligenceShellCommand extends ShellCommand {
+    private static final String TAG = OnDeviceIntelligenceShellCommand.class.getSimpleName();
+
+    @NonNull
+    private final OnDeviceIntelligenceManagerService mService;
+
+    OnDeviceIntelligenceShellCommand(@NonNull OnDeviceIntelligenceManagerService service) {
+        mService = service;
+    }
+
+    @Override
+    public int onCommand(String cmd) {
+        if (cmd == null) {
+            return handleDefaultCommands(cmd);
+        }
+
+        switch (cmd) {
+            case "set-temporary-services":
+                return setTemporaryServices();
+            case "get-services":
+                return getConfiguredServices();
+            case "set-model-broadcasts":
+                return setBroadcastKeys();
+            case "set-deviceconfig-namespace":
+                return setDeviceConfigNamespace();
+            default:
+                return handleDefaultCommands(cmd);
+        }
+    }
+
+    @Override
+    public void onHelp() {
+        PrintWriter pw = getOutPrintWriter();
+        pw.println("OnDeviceIntelligenceShellCommand commands: ");
+        pw.println("  help");
+        pw.println("    Print this help text.");
+        pw.println();
+        pw.println(
+                "  set-temporary-services [IntelligenceServiceComponentName] "
+                        + "[InferenceServiceComponentName] [DURATION]");
+        pw.println("    Temporarily (for DURATION ms) changes the service implementations.");
+        pw.println("    To reset, call without any arguments.");
+
+        pw.println("  get-services To get the names of services that are currently being used.");
+        pw.println(
+                "  set-model-broadcasts [ModelLoadedBroadcastKey] [ModelUnloadedBroadcastKey] "
+                        + "[ReceiverPackageName] "
+                        + "[DURATION] To set the names of broadcast intent keys that are to be "
+                        + "emitted for cts tests.");
+        pw.println(
+                "  set-deviceconfig-namespace [DeviceConfigNamespace] "
+                        + "[DURATION] To set the device config namespace "
+                        + "to use for cts tests.");
+    }
+
+    private int setTemporaryServices() {
+        final PrintWriter out = getOutPrintWriter();
+        final String intelligenceServiceName = getNextArg();
+        final String inferenceServiceName = getNextArg();
+
+        if (getRemainingArgsCount() == 0 && intelligenceServiceName == null
+                && inferenceServiceName == null) {
+            OnDeviceIntelligenceManagerService.enforceShellOnly(Binder.getCallingUid(),
+                    "resetTemporaryServices");
+            mService.resetTemporaryServices();
+            out.println("OnDeviceIntelligenceManagerService temporary reset. ");
+            return 0;
+        }
+
+        Objects.requireNonNull(intelligenceServiceName);
+        Objects.requireNonNull(inferenceServiceName);
+        final int duration = Integer.parseInt(getNextArgRequired());
+        mService.setTemporaryServices(
+                new String[]{intelligenceServiceName, inferenceServiceName},
+                duration);
+        out.println("OnDeviceIntelligenceService temporarily set to " + intelligenceServiceName
+                + " \n and \n OnDeviceTrustedInferenceService set to " + inferenceServiceName
+                + " for " + duration + "ms");
+        return 0;
+    }
+
+    private int getConfiguredServices() {
+        final PrintWriter out = getOutPrintWriter();
+        String[] services = mService.getServiceNames();
+        out.println("OnDeviceIntelligenceService set to :  " + services[0]
+                + " \n and \n OnDeviceTrustedInferenceService set to : " + services[1]);
+        return 0;
+    }
+
+    private int setBroadcastKeys() {
+        final PrintWriter out = getOutPrintWriter();
+        final String modelLoadedKey = getNextArgRequired();
+        final String modelUnloadedKey = getNextArgRequired();
+        final String receiverPackageName = getNextArg();
+
+        final int duration = Integer.parseInt(getNextArgRequired());
+        mService.setModelBroadcastKeys(
+                new String[]{modelLoadedKey, modelUnloadedKey}, receiverPackageName, duration);
+        out.println("OnDeviceIntelligence Model Loading broadcast keys temporarily set to "
+                + modelLoadedKey
+                + " \n and \n OnDeviceTrustedInferenceService set to " + modelUnloadedKey
+                + "\n and Package name set to : " + receiverPackageName
+                + " for " + duration + "ms");
+        return 0;
+    }
+
+    private int setDeviceConfigNamespace() {
+        final PrintWriter out = getOutPrintWriter();
+        final String configNamespace = getNextArg();
+
+        final int duration = Integer.parseInt(getNextArgRequired());
+        mService.setTemporaryDeviceConfigNamespace(configNamespace, duration);
+        out.println("OnDeviceIntelligence DeviceConfig Namespace temporarily set to "
+                + configNamespace
+                + " for " + duration + "ms");
+        return 0;
+    }
+
+}
\ No newline at end of file
diff --git a/packages/NeuralNetworks/service/platform/java/com/android/server/ondeviceintelligence/RemoteOnDeviceIntelligenceService.java b/packages/NeuralNetworks/service/platform/java/com/android/server/ondeviceintelligence/RemoteOnDeviceIntelligenceService.java
new file mode 100644
index 0000000..ac9747a
--- /dev/null
+++ b/packages/NeuralNetworks/service/platform/java/com/android/server/ondeviceintelligence/RemoteOnDeviceIntelligenceService.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.ondeviceintelligence;
+
+import static android.content.Context.BIND_FOREGROUND_SERVICE;
+import static android.content.Context.BIND_INCLUDE_CAPABILITIES;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.provider.Settings;
+import android.service.ondeviceintelligence.IOnDeviceIntelligenceService;
+import android.service.ondeviceintelligence.OnDeviceIntelligenceService;
+
+import com.android.internal.infra.ServiceConnector;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Manages the connection to the remote on-device intelligence service. Also, handles unbinding
+ * logic set by the service implementation via a Secure Settings flag.
+ */
+public class RemoteOnDeviceIntelligenceService extends
+        ServiceConnector.Impl<IOnDeviceIntelligenceService> {
+    private static final long LONG_TIMEOUT = TimeUnit.HOURS.toMillis(4);
+    private static final String TAG =
+            RemoteOnDeviceIntelligenceService.class.getSimpleName();
+
+    RemoteOnDeviceIntelligenceService(Context context, ComponentName serviceName,
+            int userId) {
+        super(context, new Intent(
+                        OnDeviceIntelligenceService.SERVICE_INTERFACE).setComponent(serviceName),
+                BIND_FOREGROUND_SERVICE | BIND_INCLUDE_CAPABILITIES, userId,
+                IOnDeviceIntelligenceService.Stub::asInterface);
+
+        // Bind right away
+        connect();
+    }
+
+    @Override
+    protected long getRequestTimeoutMs() {
+        return LONG_TIMEOUT;
+    }
+
+    @Override
+    protected long getAutoDisconnectTimeoutMs() {
+        return Settings.Secure.getLongForUser(mContext.getContentResolver(),
+                Settings.Secure.ON_DEVICE_INTELLIGENCE_UNBIND_TIMEOUT_MS,
+                TimeUnit.SECONDS.toMillis(30),
+                mContext.getUserId());
+    }
+}
diff --git a/packages/NeuralNetworks/service/platform/java/com/android/server/ondeviceintelligence/RemoteOnDeviceSandboxedInferenceService.java b/packages/NeuralNetworks/service/platform/java/com/android/server/ondeviceintelligence/RemoteOnDeviceSandboxedInferenceService.java
new file mode 100644
index 0000000..18b1383
--- /dev/null
+++ b/packages/NeuralNetworks/service/platform/java/com/android/server/ondeviceintelligence/RemoteOnDeviceSandboxedInferenceService.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.ondeviceintelligence;
+
+import static android.content.Context.BIND_FOREGROUND_SERVICE;
+import static android.content.Context.BIND_INCLUDE_CAPABILITIES;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.provider.Settings;
+import android.service.ondeviceintelligence.IOnDeviceSandboxedInferenceService;
+import android.service.ondeviceintelligence.OnDeviceSandboxedInferenceService;
+
+import com.android.internal.infra.ServiceConnector;
+
+import java.util.concurrent.TimeUnit;
+
+
+/**
+ * Manages the connection to the remote on-device sand boxed inference service. Also, handles
+ * unbinding
+ * logic set by the service implementation via a SecureSettings flag.
+ */
+public class RemoteOnDeviceSandboxedInferenceService extends
+        ServiceConnector.Impl<IOnDeviceSandboxedInferenceService> {
+    private static final long LONG_TIMEOUT = TimeUnit.HOURS.toMillis(1);
+
+    /**
+     * Creates an instance of {@link ServiceConnector}
+     *
+     * See {@code protected} methods for optional parameters you can override.
+     *
+     * @param context to be used for {@link Context#bindServiceAsUser binding} and
+     *                {@link Context#unbindService unbinding}
+     * @param userId  to be used for {@link Context#bindServiceAsUser binding}
+     */
+    RemoteOnDeviceSandboxedInferenceService(Context context, ComponentName serviceName,
+            int userId) {
+        super(context, new Intent(
+                        OnDeviceSandboxedInferenceService.SERVICE_INTERFACE).setComponent(serviceName),
+                BIND_FOREGROUND_SERVICE | BIND_INCLUDE_CAPABILITIES, userId,
+                IOnDeviceSandboxedInferenceService.Stub::asInterface);
+
+        // Bind right away
+        connect();
+    }
+
+    @Override
+    protected long getRequestTimeoutMs() {
+        return LONG_TIMEOUT;
+    }
+
+
+    @Override
+    protected long getAutoDisconnectTimeoutMs() {
+        return Settings.Secure.getLongForUser(mContext.getContentResolver(),
+                Settings.Secure.ON_DEVICE_INFERENCE_UNBIND_TIMEOUT_MS,
+                TimeUnit.SECONDS.toMillis(30),
+                mContext.getUserId());
+    }
+}
diff --git a/packages/NeuralNetworks/service/platform/java/com/android/server/ondeviceintelligence/callbacks/ListenableDownloadCallback.java b/packages/NeuralNetworks/service/platform/java/com/android/server/ondeviceintelligence/callbacks/ListenableDownloadCallback.java
new file mode 100644
index 0000000..32f0698
--- /dev/null
+++ b/packages/NeuralNetworks/service/platform/java/com/android/server/ondeviceintelligence/callbacks/ListenableDownloadCallback.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.ondeviceintelligence.callbacks;
+
+import android.app.ondeviceintelligence.IDownloadCallback;
+import android.os.Handler;
+import android.os.PersistableBundle;
+import android.os.RemoteException;
+
+import com.android.internal.infra.AndroidFuture;
+
+import java.util.concurrent.TimeoutException;
+
+/**
+ * This class extends the {@link IDownloadCallback} and adds a timeout Runnable to the callback
+ * such that, in the case where the callback methods are not invoked, we do not have to wait for
+ * timeout based on {@link #onDownloadCompleted} which might take minutes or hours to complete in
+ * some cases. Instead, in such cases we rely on the remote service sending progress updates and if
+ * there are *no* progress callbacks in the duration of {@link #idleTimeoutMs}, we can assume the
+ * download will not complete and enabling faster cleanup.
+ */
+public class ListenableDownloadCallback extends IDownloadCallback.Stub implements Runnable {
+    private final IDownloadCallback callback;
+    private final Handler handler;
+    private final AndroidFuture future;
+    private final long idleTimeoutMs;
+
+    /**
+     * Constructor to create a ListenableDownloadCallback.
+     *
+     * @param callback      callback to send download updates to caller.
+     * @param handler       handler to schedule timeout runnable.
+     * @param future        future to complete to signal the callback has reached a terminal state.
+     * @param idleTimeoutMs timeout within which download updates should be received.
+     */
+    public ListenableDownloadCallback(IDownloadCallback callback, Handler handler,
+            AndroidFuture future,
+            long idleTimeoutMs) {
+        this.callback = callback;
+        this.handler = handler;
+        this.future = future;
+        this.idleTimeoutMs = idleTimeoutMs;
+        handler.postDelayed(this,
+                idleTimeoutMs); // init the timeout runnable in case no callback is ever invoked
+    }
+
+    @Override
+    public void onDownloadStarted(long bytesToDownload) throws RemoteException {
+        callback.onDownloadStarted(bytesToDownload);
+        handler.removeCallbacks(this);
+        handler.postDelayed(this, idleTimeoutMs);
+    }
+
+    @Override
+    public void onDownloadProgress(long bytesDownloaded) throws RemoteException {
+        callback.onDownloadProgress(bytesDownloaded);
+        handler.removeCallbacks(this); // remove previously queued timeout tasks.
+        handler.postDelayed(this, idleTimeoutMs); // queue fresh timeout task for next update.
+    }
+
+    @Override
+    public void onDownloadFailed(int failureStatus,
+            String errorMessage, PersistableBundle errorParams) throws RemoteException {
+        callback.onDownloadFailed(failureStatus, errorMessage, errorParams);
+        handler.removeCallbacks(this);
+        future.completeExceptionally(new TimeoutException());
+    }
+
+    @Override
+    public void onDownloadCompleted(
+            android.os.PersistableBundle downloadParams) throws RemoteException {
+        callback.onDownloadCompleted(downloadParams);
+        handler.removeCallbacks(this);
+        future.complete(null);
+    }
+
+    @Override
+    public void run() {
+        future.completeExceptionally(
+                new TimeoutException()); // complete the future as we haven't received updates
+        // for download progress.
+    }
+}
\ No newline at end of file
diff --git a/packages/PackageInstaller/res/values-af/strings.xml b/packages/PackageInstaller/res/values-af/strings.xml
index 77fad55..ac96d7a 100644
--- a/packages/PackageInstaller/res/values-af/strings.xml
+++ b/packages/PackageInstaller/res/values-af/strings.xml
@@ -24,8 +24,8 @@
     <string name="installing" msgid="4921993079741206516">"Installeer tans …"</string>
     <string name="installing_app" msgid="1165095864863849422">"Installeer tans <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> …"</string>
     <string name="install_done" msgid="5987363587661783896">"App geïnstalleer."</string>
-    <string name="install_confirm_question" msgid="7663733664476363311">"Wil jy hierdie program installeer?"</string>
-    <string name="install_confirm_question_update" msgid="3348888852318388584">"Wil jy hierdie program opdateer?"</string>
+    <string name="install_confirm_question" msgid="7663733664476363311">"Wil jy hierdie app installeer?"</string>
+    <string name="install_confirm_question_update" msgid="3348888852318388584">"Wil jy hierdie app opdateer?"</string>
     <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="7994800761970572198">"&lt;p&gt;Dateer hierdie app op vanaf &lt;b&gt;<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>&lt;/b&gt;?&lt;/p&gt;&lt;p&gt;Hierdie app ontvang gewoonlik opdaterings vanaf &lt;b&gt;<xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>&lt;/b&gt;. As jy vanaf ’n ander bron opdateer, kan jy in die toekoms dalk opdaterings vanaf enige bron op jou tablet kry. Appfunksies kan verander.&lt;/p&gt;"</string>
     <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="2435174886412089791">"&lt;p&gt;Dateer hierdie app op vanaf &lt;b&gt;<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>&lt;/b&gt;?&lt;/p&gt;&lt;p&gt;Hierdie app ontvang gewoonlik opdaterings vanaf &lt;b&gt;<xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>&lt;/b&gt;. As jy vanaf ’n ander bron opdateer, kan jy in die toekoms dalk opdaterings vanaf enige bron op jou TV kry. Appfunksies kan verander.&lt;/p&gt;"</string>
     <string name="install_confirm_question_update_owner_reminder" product="default" msgid="7155138616126795839">"&lt;p&gt;Dateer hierdie app op vanaf &lt;b&gt;<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>&lt;/b&gt;?&lt;/p&gt;&lt;p&gt;Hierdie app ontvang gewoonlik opdaterings vanaf &lt;b&gt;<xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>&lt;/b&gt;. As jy vanaf ’n ander bron opdateer, kan jy in die toekoms dalk opdaterings vanaf enige bron op jou foon kry. Appfunksies kan verander.&lt;/p&gt;"</string>
@@ -66,9 +66,9 @@
     <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Wil jy hierdie app wat in jou privaat ruimte is, argiveer? Jou persoonlike data sal gestoor word"</string>
     <string name="uninstall_application_text_all_users" msgid="575491774380227119">"Wil jy hierdie app vir "<b>"alle"</b>" gebruikers deïnstalleer? Die app en sy data sal van "<b>"alle"</b>" gebruikers op hierdie toestel verwyder word."</string>
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Wil jy hierdie app vir die gebruiker <xliff:g id="USERNAME">%1$s</xliff:g> deïnstalleer?"</string>
-    <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Wil jy hierdie program op jou werkprofiel deïnstalleer?"</string>
+    <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Wil jy hierdie app op jou werkprofiel deïnstalleer?"</string>
     <string name="uninstall_update_text" msgid="863648314632448705">"Vervang hierdie app met die fabriekweergawe? Alle data sal verwyder word."</string>
-    <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"Vervang hierdie program met die fabriekweergawe? Alle data sal verwyder word. Dit beïnvloed alle gebruikers van hierdie toestel, insluitend dié met werkprofiele."</string>
+    <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"Vervang hierdie app met die fabriekweergawe? Alle data sal verwyder word. Dit beïnvloed alle gebruikers van hierdie toestel, insluitend dié met werkprofiele."</string>
     <string name="uninstall_keep_data" msgid="7002379587465487550">"Hou <xliff:g id="SIZE">%1$s</xliff:g> se programdata."</string>
     <string name="uninstall_application_text_current_user_clone_profile" msgid="835170400160011636">"Wil jy hierdie app uitvee?"</string>
     <string name="uninstall_application_text_with_clone_instance" msgid="6944473334273349036">"Wil jy hierdie app deïnstalleer? <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>-kloon sal ook uitgevee word."</string>
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java
index 379dfe3..635ae20 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java
@@ -36,6 +36,7 @@
 import android.os.Bundle;
 import android.os.Process;
 import android.os.UserManager;
+import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.EventLog;
 import android.util.Log;
@@ -62,9 +63,12 @@
     protected void onCreate(@Nullable Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
+        boolean testOverrideForPiaV2 = Settings.System.getInt(getContentResolver(),
+                "use_pia_v2", 0) == 1;
+        boolean usePiaV2aConfig = usePiaV2();
 
-        if (usePiaV2()) {
-            Log.i(TAG, "Using Pia V2");
+        if (usePiaV2aConfig || testOverrideForPiaV2) {
+            logReasonForDebug(usePiaV2aConfig, testOverrideForPiaV2);
 
             Intent piaV2 = new Intent(getIntent());
             piaV2.putExtra(InstallLaunch.EXTRA_CALLING_PKG_NAME, getLaunchedFromPackage());
@@ -381,4 +385,20 @@
         }
         return null;
     }
+
+    private void logReasonForDebug(boolean usePiaV2aConfig, boolean testOverrideForPiaV2) {
+        StringBuilder sb = new StringBuilder("Using Pia V2 due to: ");
+        boolean aconfigUsed = false;
+        if (usePiaV2aConfig) {
+            sb.append("aconfig flag USE_PIA_V2");
+            aconfigUsed = true;
+        }
+        if (testOverrideForPiaV2) {
+            if (aconfigUsed) {
+                sb.append(" and ");
+            }
+            sb.append("testOverrideForPiaV2.");
+        }
+        Log.i(TAG, sb.toString());
+    }
 }
diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp
index a3da93d..d739aaf 100644
--- a/packages/SettingsLib/Android.bp
+++ b/packages/SettingsLib/Android.bp
@@ -65,6 +65,7 @@
     libs:[
         // This flag library has been added in frameworks jar
         "aconfig_settingslib_flags_java_lib",
+        "wifi_framework_aconfig_flags_lib",
     ],
     plugins: ["androidx.room_room-compiler-plugin"],
     use_resource_processor: true,
diff --git a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/GetPreferenceGraphApiHandler.kt b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/GetPreferenceGraphApiHandler.kt
index 7aece51..3c8d6ed 100644
--- a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/GetPreferenceGraphApiHandler.kt
+++ b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/GetPreferenceGraphApiHandler.kt
@@ -38,11 +38,11 @@
 
     override suspend fun invoke(
         application: Application,
-        myUid: Int,
+        callingPid: Int,
         callingUid: Int,
         request: GetPreferenceGraphRequest,
     ): PreferenceGraphProto {
-        val builder = PreferenceGraphBuilder.of(application, myUid, callingUid, request)
+        val builder = PreferenceGraphBuilder.of(application, callingPid, callingUid, request)
         if (request.screenKeys.isEmpty()) {
             for (key in PreferenceScreenRegistry.preferenceScreens.keys) {
                 builder.addPreferenceScreenFromRegistry(key)
diff --git a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGetterApi.kt b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGetterApi.kt
index c8453ef..de5731e 100644
--- a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGetterApi.kt
+++ b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGetterApi.kt
@@ -83,14 +83,14 @@
 
     override fun hasPermission(
         application: Application,
-        myUid: Int,
+        callingPid: Int,
         callingUid: Int,
         request: PreferenceGetterRequest,
-    ) = permissionChecker.hasPermission(application, myUid, callingUid, request)
+    ) = permissionChecker.hasPermission(application, callingPid, callingUid, request)
 
     override suspend fun invoke(
         application: Application,
-        myUid: Int,
+        callingPid: Int,
         callingUid: Int,
         request: PreferenceGetterRequest,
     ): PreferenceGetterResponse {
@@ -123,7 +123,7 @@
                     val preferenceProto =
                         metadata.toProto(
                             application,
-                            myUid,
+                            callingPid,
                             callingUid,
                             screenMetadata,
                             metadata.key == screenMetadata.key,
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 606710e..eaa7926 100644
--- a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGraphBuilder.kt
+++ b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGraphBuilder.kt
@@ -22,6 +22,7 @@
 import android.content.Context
 import android.content.Intent
 import android.content.pm.PackageManager
+import android.content.pm.PackageManager.PERMISSION_GRANTED
 import android.content.res.Configuration
 import android.os.Build
 import android.os.Bundle
@@ -65,7 +66,7 @@
 class PreferenceGraphBuilder
 private constructor(
     private val context: Context,
-    private val myUid: Int,
+    private val callingPid: Int,
     private val callingUid: Int,
     private val request: GetPreferenceGraphRequest,
 ) {
@@ -81,7 +82,7 @@
         }
     }
 
-    fun build() = builder.build()
+    fun build(): PreferenceGraphProto = builder.build()
 
     /**
      * Adds an activity to the graph.
@@ -268,16 +269,18 @@
         metadata: PreferenceMetadata,
         isRoot: Boolean,
     ) =
-        metadata.toProto(context, myUid, callingUid, screenMetadata, isRoot, request.flags).also {
-            if (metadata is PreferenceScreenMetadata) {
-                @Suppress("CheckReturnValue") addPreferenceScreenMetadata(metadata)
-            }
-            metadata.intent(context)?.resolveActivity(context.packageManager)?.let {
-                if (it.packageName == context.packageName) {
-                    add(it.className)
+        metadata
+            .toProto(context, callingPid, callingUid, screenMetadata, isRoot, request.flags)
+            .also {
+                if (metadata is PreferenceScreenMetadata) {
+                    @Suppress("CheckReturnValue") addPreferenceScreenMetadata(metadata)
+                }
+                metadata.intent(context)?.resolveActivity(context.packageManager)?.let {
+                    if (it.packageName == context.packageName) {
+                        add(it.className)
+                    }
                 }
             }
-        }
 
     private suspend fun String?.toActionTarget(extras: Bundle?): ActionTarget? {
         if (this.isNullOrEmpty()) return null
@@ -343,16 +346,16 @@
     companion object {
         suspend fun of(
             context: Context,
-            myUid: Int,
+            callingPid: Int,
             callingUid: Int,
             request: GetPreferenceGraphRequest,
-        ) = PreferenceGraphBuilder(context, myUid, callingUid, request).also { it.init() }
+        ) = PreferenceGraphBuilder(context, callingPid, callingUid, request).also { it.init() }
     }
 }
 
 fun PreferenceMetadata.toProto(
     context: Context,
-    myUid: Int,
+    callingPid: Int,
     callingUid: Int,
     screenMetadata: PreferenceScreenMetadata,
     isRoot: Boolean,
@@ -394,7 +397,7 @@
                 (!hasAvailable() || available) &&
                 (!hasRestricted() || !restricted) &&
                 metadata is PersistentPreference<*> &&
-                metadata.getReadPermit(context, myUid, callingUid) == ReadWritePermit.ALLOW
+                metadata.evalReadPermit(context, callingPid, callingUid) == ReadWritePermit.ALLOW
         ) {
             value = preferenceValueProto {
                 when (metadata) {
@@ -425,6 +428,20 @@
     }
 }
 
+/** Evaluates the read permit of a persistent preference. */
+fun <T> PersistentPreference<T>.evalReadPermit(
+    context: Context,
+    callingPid: Int,
+    callingUid: Int,
+): Int {
+    for (permission in getReadPermissions(context)) {
+        if (context.checkPermission(permission, callingPid, callingUid) != PERMISSION_GRANTED) {
+            return ReadWritePermit.REQUIRE_APP_PERMISSION
+        }
+    }
+    return getReadPermit(context, callingPid, callingUid)
+}
+
 private fun PreferenceMetadata.getTitleTextProto(context: Context, isRoot: Boolean): TextProto? {
     if (isRoot && this is PreferenceScreenMetadata) {
         val titleRes = screenTitle
diff --git a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceSetterApi.kt b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceSetterApi.kt
index 56b1693..d72ba08 100644
--- a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceSetterApi.kt
+++ b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceSetterApi.kt
@@ -17,6 +17,8 @@
 package com.android.settingslib.graph
 
 import android.app.Application
+import android.content.Context
+import android.content.pm.PackageManager.PERMISSION_GRANTED
 import android.os.Bundle
 import androidx.annotation.IntDef
 import com.android.settingslib.graph.proto.PreferenceValueProto
@@ -99,14 +101,14 @@
 
     override fun hasPermission(
         application: Application,
-        myUid: Int,
+        callingPid: Int,
         callingUid: Int,
         request: PreferenceSetterRequest,
-    ) = permissionChecker.hasPermission(application, myUid, callingUid, request)
+    ) = permissionChecker.hasPermission(application, callingPid, callingUid, request)
 
     override suspend fun invoke(
         application: Application,
-        myUid: Int,
+        callingPid: Int,
         callingUid: Int,
         request: PreferenceSetterRequest,
     ): Int {
@@ -127,7 +129,7 @@
 
         fun <T> PreferenceMetadata.checkWritePermit(value: T): Int {
             @Suppress("UNCHECKED_CAST") val preference = (this as PersistentPreference<T>)
-            return when (preference.getWritePermit(application, value, myUid, callingUid)) {
+            return when (preference.evalWritePermit(application, value, callingPid, callingUid)) {
                 ReadWritePermit.ALLOW -> PreferenceSetterResult.OK
                 ReadWritePermit.DISALLOW -> PreferenceSetterResult.DISALLOW
                 ReadWritePermit.REQUIRE_APP_PERMISSION ->
@@ -171,6 +173,21 @@
         get() = IntMessageCodec
 }
 
+/** Evaluates the write permit of a persistent preference. */
+fun <T> PersistentPreference<T>.evalWritePermit(
+    context: Context,
+    value: T?,
+    callingPid: Int,
+    callingUid: Int,
+): Int {
+    for (permission in getWritePermissions(context)) {
+        if (context.checkPermission(permission, callingPid, callingUid) != PERMISSION_GRANTED) {
+            return ReadWritePermit.REQUIRE_APP_PERMISSION
+        }
+    }
+    return getWritePermit(context, value, callingPid, callingUid)
+}
+
 /** Message codec for [PreferenceSetterRequest]. */
 object PreferenceSetterRequestCodec : MessageCodec<PreferenceSetterRequest> {
     override fun encode(data: PreferenceSetterRequest) =
diff --git a/packages/SettingsLib/Ipc/README.md b/packages/SettingsLib/Ipc/README.md
index ea2c3a1b5..719d01e1 100644
--- a/packages/SettingsLib/Ipc/README.md
+++ b/packages/SettingsLib/Ipc/README.md
@@ -101,14 +101,14 @@
                      ApiDescriptor<String?, String?> by EchoApi {
   override suspend fun invoke(
     application: Application,
-    myUid: Int,
+    callingPid: Int,
     callingUid: Int,
     request: String?,
   ): String? = request
 
   override fun hasPermission(
     application: Application,
-    myUid: Int,
+    callingPid: Int,
     callingUid: Int,
     request: String?,
   ): Boolean = (request?.length ?: 0) <= 5
diff --git a/packages/SettingsLib/Ipc/src/com/android/settingslib/ipc/ApiHandler.kt b/packages/SettingsLib/Ipc/src/com/android/settingslib/ipc/ApiHandler.kt
index 4febd89..6d746e020 100644
--- a/packages/SettingsLib/Ipc/src/com/android/settingslib/ipc/ApiHandler.kt
+++ b/packages/SettingsLib/Ipc/src/com/android/settingslib/ipc/ApiHandler.kt
@@ -62,12 +62,17 @@
      * Returns if the request is permitted.
      *
      * @param application application context
-     * @param myUid uid of current process
+     * @param callingPid pid of peer process
      * @param callingUid uid of peer process
      * @param request API request
      * @return `false` if permission is denied, otherwise `true`
      */
-    fun hasPermission(application: Application, myUid: Int, callingUid: Int, request: R): Boolean
+    fun hasPermission(
+        application: Application,
+        callingPid: Int,
+        callingUid: Int,
+        request: R,
+    ): Boolean
 
     companion object {
         private val ALWAYS_ALLOW = ApiPermissionChecker<Any> { _, _, _, _ -> true }
@@ -96,7 +101,7 @@
      */
     suspend fun invoke(
         application: Application,
-        myUid: Int,
+        callingPid: Int,
         callingUid: Int,
         request: Request,
     ): Response
diff --git a/packages/SettingsLib/Ipc/src/com/android/settingslib/ipc/MessengerService.kt b/packages/SettingsLib/Ipc/src/com/android/settingslib/ipc/MessengerService.kt
index 0bdae38..7c80b59 100644
--- a/packages/SettingsLib/Ipc/src/com/android/settingslib/ipc/MessengerService.kt
+++ b/packages/SettingsLib/Ipc/src/com/android/settingslib/ipc/MessengerService.kt
@@ -25,7 +25,6 @@
 import android.os.Looper
 import android.os.Message
 import android.os.Messenger
-import android.os.Process
 import android.util.Log
 import androidx.annotation.VisibleForTesting
 import kotlinx.coroutines.CoroutineScope
@@ -92,7 +91,6 @@
         private val apiHandlers: Array<ApiHandler<*, *>>,
         private val permissionChecker: PermissionChecker,
     ) : Handler(looper) {
-        @VisibleForTesting internal val myUid = Process.myUid()
         val coroutineScope = CoroutineScope(asCoroutineDispatcher().immediate + SupervisorJob())
 
         override fun handleMessage(msg: Message) {
@@ -109,18 +107,22 @@
             }
             val apiId = msg.what
             val txnId = msg.arg1
+            val callingPid = msg.arg2
             val callingUid = msg.sendingUid
             val data = msg.data
             // WARNING: never access "msg" beyond this point as it may be recycled by Looper
             val response = Message.obtain(null, apiId, txnId, ApiServiceException.CODE_OK)
             try {
-                if (permissionChecker.check(application, myUid, callingUid)) {
+                if (permissionChecker.check(application, callingPid, callingUid)) {
                     @Suppress("UNCHECKED_CAST")
                     val apiHandler = findApiHandler(apiId) as? ApiHandler<Any, Any>
                     if (apiHandler != null) {
                         val request = apiHandler.requestCodec.decode(data)
-                        if (apiHandler.hasPermission(application, myUid, callingUid, request)) {
-                            val result = apiHandler.invoke(application, myUid, callingUid, request)
+                        if (
+                            apiHandler.hasPermission(application, callingPid, callingUid, request)
+                        ) {
+                            val result =
+                                apiHandler.invoke(application, callingPid, callingUid, request)
                             response.data = apiHandler.responseCodec.encode(result)
                         } else {
                             response.arg2 = ApiServiceException.CODE_PERMISSION_DENIED
diff --git a/packages/SettingsLib/Ipc/src/com/android/settingslib/ipc/MessengerServiceClient.kt b/packages/SettingsLib/Ipc/src/com/android/settingslib/ipc/MessengerServiceClient.kt
index ef907e1..3f7eea5 100644
--- a/packages/SettingsLib/Ipc/src/com/android/settingslib/ipc/MessengerServiceClient.kt
+++ b/packages/SettingsLib/Ipc/src/com/android/settingslib/ipc/MessengerServiceClient.kt
@@ -28,6 +28,7 @@
 import android.os.Looper
 import android.os.Message
 import android.os.Messenger
+import android.os.Process
 import android.util.Log
 import androidx.annotation.OpenForTesting
 import androidx.annotation.VisibleForTesting
@@ -190,6 +191,7 @@
         private val metricsLogger: MetricsLogger?,
     ) : Handler(looper), ServiceConnection {
         private val clientMessenger = Messenger(this)
+        internal val myPid = Process.myPid()
         internal val pendingRequests = ArrayDeque<RequestWrapper<*, *>>()
         internal var serviceMessenger: Messenger? = null
         internal open var connectionState: Int = STATE_INIT
@@ -364,7 +366,7 @@
                 drainPendingRequests()
             }
             val message =
-                obtainMessage(request.apiDescriptor.id, request.txnId, 0).apply {
+                obtainMessage(request.apiDescriptor.id, request.txnId, myPid).apply {
                     replyTo = clientMessenger
                 }
             try {
diff --git a/packages/SettingsLib/Ipc/src/com/android/settingslib/ipc/PermissionChecker.kt b/packages/SettingsLib/Ipc/src/com/android/settingslib/ipc/PermissionChecker.kt
index da9c955..6653b66 100644
--- a/packages/SettingsLib/Ipc/src/com/android/settingslib/ipc/PermissionChecker.kt
+++ b/packages/SettingsLib/Ipc/src/com/android/settingslib/ipc/PermissionChecker.kt
@@ -18,6 +18,7 @@
 
 import android.app.Application
 import android.content.pm.PackageManager
+import android.os.Process
 import androidx.collection.mutableIntIntMapOf
 
 /** Checker for permission. */
@@ -26,18 +27,18 @@
      * Checks permission.
      *
      * @param application application context
-     * @param myUid uid of current process
+     * @param callingPid uid of peer process
      * @param callingUid uid of peer process
      */
-    fun check(application: Application, myUid: Int, callingUid: Int): Boolean
+    fun check(application: Application, callingPid: Int, callingUid: Int): Boolean
 }
 
 /** Verifies apk signatures as permission check. */
 class SignatureChecker : PermissionChecker {
     private val cache = mutableIntIntMapOf()
 
-    override fun check(application: Application, myUid: Int, callingUid: Int): Boolean =
+    override fun check(application: Application, callingPid: Int, callingUid: Int): Boolean =
         cache.getOrPut(callingUid) {
-            application.packageManager.checkSignatures(myUid, callingUid)
+            application.packageManager.checkSignatures(Process.myUid(), callingUid)
         } == PackageManager.SIGNATURE_MATCH
 }
diff --git a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PersistentPreference.kt b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PersistentPreference.kt
index 668f981..d3a7316 100644
--- a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PersistentPreference.kt
+++ b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PersistentPreference.kt
@@ -29,6 +29,7 @@
     ReadWritePermit.REQUIRE_USER_AGREEMENT,
 )
 @Retention(AnnotationRetention.SOURCE)
+@Target(AnnotationTarget.TYPE)
 annotation class ReadWritePermit {
     companion object {
         /** Allow to read/write value. */
@@ -67,35 +68,46 @@
     fun storage(context: Context): KeyValueStore =
         PreferenceScreenRegistry.getKeyValueStore(context, this as PreferenceMetadata)!!
 
+    /** Returns the required permissions to read preference value. */
+    fun getReadPermissions(context: Context): Array<String> = arrayOf()
+
     /**
-     * Returns if the external application (identified by [callingUid]) has permission to read
-     * preference value.
+     * Returns if the external application (identified by [callingPid] and [callingUid]) is
+     * permitted to read preference value.
      *
      * The underlying implementation does NOT need to check common states like isEnabled,
-     * isRestricted or isAvailable.
+     * isRestricted, isAvailable or permissions in [getReadPermissions]. The framework will do it
+     * behind the scene.
      */
-    @ReadWritePermit
-    fun getReadPermit(context: Context, myUid: Int, callingUid: Int): Int =
+    fun getReadPermit(context: Context, callingPid: Int, callingUid: Int): @ReadWritePermit Int =
         PreferenceScreenRegistry.getReadPermit(
             context,
-            myUid,
+            callingPid,
             callingUid,
             this as PreferenceMetadata,
         )
 
+    /** Returns the required permissions to write preference value. */
+    fun getWritePermissions(context: Context): Array<String> = arrayOf()
+
     /**
-     * Returns if the external application (identified by [callingUid]) has permission to write
-     * preference with given [value].
+     * Returns if the external application (identified by [callingPid] and [callingUid]) is
+     * permitted to write preference with given [value].
      *
      * The underlying implementation does NOT need to check common states like isEnabled,
-     * isRestricted or isAvailable.
+     * isRestricted, isAvailable or permissions in [getWritePermissions]. The framework will do it
+     * behind the scene.
      */
-    @ReadWritePermit
-    fun getWritePermit(context: Context, value: T?, myUid: Int, callingUid: Int): Int =
+    fun getWritePermit(
+        context: Context,
+        value: T?,
+        callingPid: Int,
+        callingUid: Int,
+    ): @ReadWritePermit Int =
         PreferenceScreenRegistry.getWritePermit(
             context,
             value,
-            myUid,
+            callingPid,
             callingUid,
             this as PreferenceMetadata,
         )
diff --git a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceScreenRegistry.kt b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceScreenRegistry.kt
index 6646d6c3..ff09910 100644
--- a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceScreenRegistry.kt
+++ b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceScreenRegistry.kt
@@ -37,7 +37,8 @@
     val preferenceScreens: PreferenceScreenMap
         get() = preferenceScreensSupplier.get()
 
-    private var readWritePermitProvider: ReadWritePermitProvider? = null
+    private var readWritePermitProvider: ReadWritePermitProvider =
+        object : ReadWritePermitProvider {}
 
     /** Sets the [KeyValueStoreProvider]. */
     fun setKeyValueStoreProvider(keyValueStoreProvider: KeyValueStoreProvider) {
@@ -77,28 +78,24 @@
     /**
      * Sets the provider to check read write permit. Read and write requests are denied by default.
      */
-    fun setReadWritePermitProvider(readWritePermitProvider: ReadWritePermitProvider?) {
+    fun setReadWritePermitProvider(readWritePermitProvider: ReadWritePermitProvider) {
         this.readWritePermitProvider = readWritePermitProvider
     }
 
     override fun getReadPermit(
         context: Context,
-        myUid: Int,
+        callingPid: Int,
         callingUid: Int,
         preference: PreferenceMetadata,
-    ) =
-        readWritePermitProvider?.getReadPermit(context, myUid, callingUid, preference)
-            ?: ReadWritePermit.DISALLOW
+    ) = readWritePermitProvider.getReadPermit(context, callingPid, callingUid, preference)
 
     override fun getWritePermit(
         context: Context,
         value: Any?,
-        myUid: Int,
+        callingPid: Int,
         callingUid: Int,
         preference: PreferenceMetadata,
-    ) =
-        readWritePermitProvider?.getWritePermit(context, value, myUid, callingUid, preference)
-            ?: ReadWritePermit.DISALLOW
+    ) = readWritePermitProvider.getWritePermit(context, value, callingPid, callingUid, preference)
 }
 
 /** Provider of [KeyValueStore]. */
@@ -117,41 +114,21 @@
 /** Provider of read and write permit. */
 interface ReadWritePermitProvider {
 
-    @ReadWritePermit
+    val defaultReadWritePermit: @ReadWritePermit Int
+        get() = ReadWritePermit.DISALLOW
+
     fun getReadPermit(
         context: Context,
-        myUid: Int,
+        callingPid: Int,
         callingUid: Int,
         preference: PreferenceMetadata,
-    ): Int
+    ): @ReadWritePermit Int = defaultReadWritePermit
 
-    @ReadWritePermit
     fun getWritePermit(
         context: Context,
         value: Any?,
-        myUid: Int,
+        callingPid: Int,
         callingUid: Int,
         preference: PreferenceMetadata,
-    ): Int
-
-    companion object {
-        @JvmField
-        val ALLOW_ALL_READ_WRITE =
-            object : ReadWritePermitProvider {
-                override fun getReadPermit(
-                    context: Context,
-                    myUid: Int,
-                    callingUid: Int,
-                    preference: PreferenceMetadata,
-                ) = ReadWritePermit.ALLOW
-
-                override fun getWritePermit(
-                    context: Context,
-                    value: Any?,
-                    myUid: Int,
-                    callingUid: Int,
-                    preference: PreferenceMetadata,
-                ) = ReadWritePermit.ALLOW
-            }
-    }
+    ): @ReadWritePermit Int = defaultReadWritePermit
 }
diff --git a/packages/SettingsLib/Service/src/com/android/settingslib/service/PreferenceGraphApi.kt b/packages/SettingsLib/Service/src/com/android/settingslib/service/PreferenceGraphApi.kt
index 1823ba6..ae9642a 100644
--- a/packages/SettingsLib/Service/src/com/android/settingslib/service/PreferenceGraphApi.kt
+++ b/packages/SettingsLib/Service/src/com/android/settingslib/service/PreferenceGraphApi.kt
@@ -33,8 +33,8 @@
 
     override fun hasPermission(
         application: Application,
-        myUid: Int,
+        callingPid: Int,
         callingUid: Int,
         request: GetPreferenceGraphRequest,
-    ) = permissionChecker.hasPermission(application, myUid, callingUid, request)
+    ) = permissionChecker.hasPermission(application, callingPid, callingUid, request)
 }
diff --git a/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/SettingsSpinnerPreference.java b/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/SettingsSpinnerPreference.java
index 63fe1b5..e173c5e 100644
--- a/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/SettingsSpinnerPreference.java
+++ b/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/SettingsSpinnerPreference.java
@@ -25,13 +25,15 @@
 import androidx.preference.Preference;
 import androidx.preference.Preference.OnPreferenceClickListener;
 import androidx.preference.PreferenceViewHolder;
+
 import com.android.settingslib.widget.spinner.R;
 
 /**
  * This preference uses Spinner & SettingsSpinnerAdapter which provide default layouts for
  * both view and drop down view of the Spinner.
  */
-public class SettingsSpinnerPreference extends Preference implements OnPreferenceClickListener {
+public class SettingsSpinnerPreference extends Preference
+        implements OnPreferenceClickListener, GroupSectionDividerMixin {
 
     private SettingsSpinnerAdapter mAdapter;
     private AdapterView.OnItemSelectedListener mListener;
diff --git a/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsBasePreferenceFragment.kt b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsBasePreferenceFragment.kt
index 265c065..bfaeb42 100644
--- a/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsBasePreferenceFragment.kt
+++ b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsBasePreferenceFragment.kt
@@ -16,6 +16,9 @@
 
 package com.android.settingslib.widget
 
+import android.os.Bundle
+import android.view.View
+import androidx.annotation.CallSuper
 import androidx.preference.PreferenceFragmentCompat
 import androidx.preference.PreferenceScreen
 import androidx.recyclerview.widget.RecyclerView
@@ -23,9 +26,18 @@
 /** Base class for Settings to use PreferenceFragmentCompat */
 abstract class SettingsBasePreferenceFragment : PreferenceFragmentCompat() {
 
+    @CallSuper
+    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+        super.onViewCreated(view, savedInstanceState)
+        if (SettingsThemeHelper.isExpressiveTheme(requireContext())) {
+            // Don't allow any divider in between the preferences in expressive design.
+            setDivider(null)
+        }
+    }
+
     override fun onCreateAdapter(preferenceScreen: PreferenceScreen): RecyclerView.Adapter<*> {
         if (SettingsThemeHelper.isExpressiveTheme(requireContext()))
             return SettingsPreferenceGroupAdapter(preferenceScreen)
         return super.onCreateAdapter(preferenceScreen)
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
index ac436ad..9af8ba8 100644
--- a/packages/SettingsLib/res/values-af/strings.xml
+++ b/packages/SettingsLib/res/values-af/strings.xml
@@ -621,7 +621,7 @@
     <string name="shared_data_delete_failure_text" msgid="3842701391009628947">"Kon nie die gedeelde data uitvee nie."</string>
     <string name="shared_data_no_accessors_dialog_text" msgid="8903738462570715315">"Daar is geen huurkontrakte vir hierdie gedeelde data verkry nie. Wil jy dit uitvee?"</string>
     <string name="accessor_info_title" msgid="8289823651512477787">"Programme wat data deel"</string>
-    <string name="accessor_no_description_text" msgid="7510967452505591456">"Geen beskrywing is deur die program voorsien nie."</string>
+    <string name="accessor_no_description_text" msgid="7510967452505591456">"Geen beskrywing is deur die app voorsien nie."</string>
     <string name="accessor_expires_text" msgid="4625619273236786252">"Huurkontrak verval op <xliff:g id="DATE">%s</xliff:g>"</string>
     <string name="delete_blob_text" msgid="2819192607255625697">"Vee gedeelde data uit?"</string>
     <string name="delete_blob_confirmation_text" msgid="7807446938920827280">"Is jy seker jy wil hierdie gedeelde data uitvee?"</string>
diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml
index 0b933a3..cd6cfdf 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -239,7 +239,7 @@
     <string name="category_work" msgid="4014193632325996115">"Աշխատանքային"</string>
     <string name="category_private" msgid="4244892185452788977">"Մասնավոր"</string>
     <string name="category_clone" msgid="1554511758987195974">"Կլոն"</string>
-    <string name="development_settings_title" msgid="140296922921597393">"Մշակողի ընտրանքներ"</string>
+    <string name="development_settings_title" msgid="140296922921597393">"Ծրագրավորողի ընտրանքներ"</string>
     <string name="development_settings_enable" msgid="4285094651288242183">"Միացնել մշակողի ընտրանքները"</string>
     <string name="development_settings_summary" msgid="8718917813868735095">"Կարգավորել ընտրանքները ծրագրի ծրագրավորման համար"</string>
     <string name="development_settings_not_available" msgid="355070198089140951">"Ծրագրավորման ընտրանքներն այլևս հասանելի չեն այս օգտատիրոջ"</string>
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index 7248999..87e03dc 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -128,7 +128,7 @@
     <string name="bluetooth_profile_hid" msgid="2969922922664315866">"输入设备"</string>
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"互联网连接"</string>
     <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"允许访问通讯录和通话记录"</string>
-    <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"信息将用于来电通知等用途"</string>
+    <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"这些信息将用于来电通知等用途"</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"共享互联网连接"</string>
     <string name="bluetooth_profile_map" msgid="8907204701162107271">"短信"</string>
     <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM 卡访问权限"</string>
diff --git a/packages/SettingsLib/res/values/arrays.xml b/packages/SettingsLib/res/values/arrays.xml
index 5a4d3ce..3d3dad3 100644
--- a/packages/SettingsLib/res/values/arrays.xml
+++ b/packages/SettingsLib/res/values/arrays.xml
@@ -667,4 +667,25 @@
         <item>3</item>
     </string-array>
 
+    <!-- Options for showing shade on external display for developers -->
+    <string-array name="shade_display_awareness_entries" >
+        <item>Device display only (Default)</item>
+        <item>External display</item>
+        <item>Focus-based</item>
+    </string-array>
+
+    <!-- Options for showing shade on external display for developers -->
+    <string-array name="shade_display_awareness_summaries" >
+        <item>Show shade on device display only </item>
+        <item>Show device on single external display</item>
+        <item>Show device on last focused display</item>
+    </string-array>
+
+    <!-- Values for showing shade on external display for developers -->
+    <string-array name="shade_display_awareness_values" >
+        <item>default_display</item>
+        <item>any_external_display</item>
+        <item>status_bar_latest_touch</item>
+    </string-array>
+
 </resources>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index eaf155d..e1929b7 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -990,6 +990,9 @@
     <!-- UI debug setting: simulate secondary display devices using overlays [CHAR LIMIT=45] -->
     <string name="overlay_display_devices_title">Simulate secondary displays</string>
 
+    <!-- UI debug setting: shade display awareness title [CHAR LIMIT=45] -->
+    <string name="shade_display_awareness_title">Shade display position</string>
+
     <!-- Preference category for application debugging development settings. [CHAR LIMIT=25] -->
     <string name="debug_applications_category">Apps</string>
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index 257c935..b4afb7d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -1916,6 +1916,25 @@
                 BluetoothProfile.STATE_CONNECTED;
     }
 
+    /**
+     * @return {@code true} if {@code cachedBluetoothDevice} supports broadcast assistant profile
+     */
+    public boolean isConnectedLeAudioBroadcastAssistantDevice() {
+        LocalBluetoothLeBroadcastAssistant leBroadcastAssistant =
+                mProfileManager.getLeAudioBroadcastAssistantProfile();
+        return leBroadcastAssistant != null && leBroadcastAssistant.getConnectionStatus(mDevice)
+                == BluetoothProfile.STATE_CONNECTED;
+    }
+
+    /**
+     * @return {@code true} if {@code cachedBluetoothDevice} supports volume control profile
+     */
+    public boolean isConnectedVolumeControlDevice() {
+        VolumeControlProfile volumeControl = mProfileManager.getVolumeControlProfile();
+        return volumeControl != null && volumeControl.getConnectionStatus(mDevice)
+                == BluetoothProfile.STATE_CONNECTED;
+    }
+
     private boolean isConnectedSapDevice() {
         SapProfile sapProfile = mProfileManager.getSapProfile();
         return sapProfile != null && sapProfile.getConnectionStatus(mDevice)
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.kt b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.kt
index a7e0464..e01f279 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.kt
@@ -27,6 +27,7 @@
 import android.net.wifi.sharedconnectivity.app.NetworkProviderInfo
 import android.os.Bundle
 import android.os.SystemClock
+import android.security.advancedprotection.AdvancedProtectionManager
 import android.util.Log
 import android.view.WindowManager
 import androidx.annotation.VisibleForTesting
@@ -498,7 +499,13 @@
         ): Job =
             coroutineScope.launch {
                 val wifiManager = context.getSystemService(WifiManager::class.java) ?: return@launch
-                if (wifiManager.isWepSupported == true && wifiManager.queryWepAllowed()) {
+                val aapmManager = context.getSystemService(AdvancedProtectionManager::class.java)
+                if (isAdvancedProtectionEnabled(aapmManager)) {
+                    val intent = aapmManager.createSupportIntent(
+                        AdvancedProtectionManager.FEATURE_ID_DISALLOW_WEP,
+                        AdvancedProtectionManager.SUPPORT_DIALOG_TYPE_BLOCKED_INTERACTION)
+                    onStartActivity(intent)
+                } else if (wifiManager.isWepSupported == true && wifiManager.queryWepAllowed()) {
                     onAllowed()
                 } else {
                     val intent = Intent(Intent.ACTION_MAIN).apply {
@@ -522,6 +529,18 @@
                 }
             }
 
+        private suspend fun isAdvancedProtectionEnabled(
+            aapmManager: AdvancedProtectionManager?
+        ): Boolean =
+            if (android.security.Flags.aapmApi() &&
+                    com.android.wifi.flags.Flags.wepDisabledInApm() &&
+                    aapmManager != null
+            ) {
+                withContext(Dispatchers.Default) { aapmManager.isAdvancedProtectionEnabled() }
+            } else {
+                false
+            }
+
         const val SSID = "ssid"
         const val DIALOG_WINDOW_TYPE = "dialog_window_type"
     }
diff --git a/packages/SettingsLib/tests/robotests/Android.bp b/packages/SettingsLib/tests/robotests/Android.bp
index f380e7f..81358ca 100644
--- a/packages/SettingsLib/tests/robotests/Android.bp
+++ b/packages/SettingsLib/tests/robotests/Android.bp
@@ -76,7 +76,6 @@
     tools: ["soong_zip"],
     cmd: "mkdir -p $(genDir)/META-INF/services/ && touch $(genDir)/META-INF/services/org.robolectric.internal.ShadowProvider &&" +
         "echo -e 'org.robolectric.Shadows' >> $(genDir)/META-INF/services/org.robolectric.internal.ShadowProvider && " +
-        "echo -e 'org.robolectric.shadows.multidex.Shadows' >> $(genDir)/META-INF/services/org.robolectric.internal.ShadowProvider && " +
         "echo -e 'org.robolectric.shadows.httpclient.Shadows' >> $(genDir)/META-INF/services/org.robolectric.internal.ShadowProvider && " +
         //"echo -e 'com.android.settings.testutils.shadow.Shadows' >> $(genDir)/META-INF/services/org.robolectric.internal.ShadowProvider && " +
         "echo -e 'com.android.settingslib.testutils.shadow.Shadows' >> $(genDir)/META-INF/services/org.robolectric.internal.ShadowProvider && " +
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index 731cb72..18bebd4 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -52,6 +52,7 @@
         Settings.Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT,
         Settings.Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN,
         Settings.Secure.ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN,
+        Settings.Secure.ACCESSIBILITY_HCT_RECT_PROMPT_STATUS,
         Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED,
         Settings.Secure.CONTRAST_LEVEL,
         Settings.Secure.ACCESSIBILITY_CAPTIONING_PRESET,
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index 039832c..1d7608d 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -88,6 +88,9 @@
         VALIDATORS.put(Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(
+                Secure.ACCESSIBILITY_HCT_RECT_PROMPT_STATUS,
+                new DiscreteValueValidator(new String[] {"0", "1", "2"}));
         VALIDATORS.put(Secure.CONTRAST_LEVEL, new InclusiveFloatRangeValidator(-1f, 1f));
         VALIDATORS.put(
                 Secure.ACCESSIBILITY_CAPTIONING_PRESET,
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
index 326bff4..1c4def3 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
@@ -35,6 +35,7 @@
 import android.net.wifi.SoftApConfiguration;
 import android.net.wifi.WifiManager;
 import android.os.Build;
+import android.os.LocaleList;
 import android.os.ParcelFileDescriptor;
 import android.os.UserHandle;
 import android.provider.Settings;
@@ -269,7 +270,7 @@
         byte[] secureSettingsData = getSecureSettings();
         byte[] globalSettingsData = getGlobalSettings();
         byte[] lockSettingsData   = getLockSettings(UserHandle.myUserId());
-        byte[] locale = mSettingsHelper.getLocaleData();
+        byte[] locale = getLocaleSettings();
         byte[] softApConfigData = getSoftAPConfiguration();
         byte[] netPoliciesData = getNetworkPolicies();
         byte[] wifiFullConfigData = getNewWifiConfigData();
@@ -408,7 +409,12 @@
                 case KEY_LOCALE :
                     byte[] localeData = new byte[size];
                     data.readEntityData(localeData, 0, size);
-                    mSettingsHelper.setLocaleData(localeData, size);
+                    mSettingsHelper
+                        .setLocaleData(
+                            localeData,
+                            size,
+                            mBackupRestoreEventLogger,
+                            KEY_LOCALE);
                     break;
 
                 case KEY_WIFI_CONFIG :
@@ -545,7 +551,9 @@
             if (DEBUG_BACKUP) Log.d(TAG, nBytes + " bytes of locale data");
             if (nBytes > buffer.length) buffer = new byte[nBytes];
             in.readFully(buffer, 0, nBytes);
-            mSettingsHelper.setLocaleData(buffer, nBytes);
+            mSettingsHelper
+                .setLocaleData(
+                    buffer, nBytes, mBackupRestoreEventLogger, KEY_LOCALE);
 
             // Restore older backups performing the necessary migrations.
             if (version < FULL_BACKUP_ADDED_WIFI_NEW) {
@@ -1410,6 +1418,15 @@
         return mWifiManager.retrieveBackupData();
     }
 
+    private byte[] getLocaleSettings() {
+        if (!areAgentMetricsEnabled) {
+            return mSettingsHelper.getLocaleData();
+        }
+        LocaleList localeList = mSettingsHelper.getLocaleList();
+        numberOfSettingsPerKey.put(KEY_LOCALE, localeList.size());
+        return localeList.toLanguageTags().getBytes();
+    }
+
     private void restoreNewWifiConfigData(byte[] bytes) {
         if (DEBUG_BACKUP) {
             Log.v(TAG, "Applying restored wifi data");
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
index ea8ae7b..924c151 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
@@ -19,11 +19,14 @@
 import android.annotation.NonNull;
 import android.app.ActivityManager;
 import android.app.IActivityManager;
+import android.app.backup.BackupRestoreEventLogger;
 import android.app.backup.IBackupManager;
 import android.content.ContentResolver;
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
 import android.content.res.Configuration;
 import android.hardware.display.ColorDisplayManager;
 import android.icu.util.ULocale;
@@ -31,6 +34,7 @@
 import android.media.RingtoneManager;
 import android.media.Utils;
 import android.net.Uri;
+import android.os.Build;
 import android.os.LocaleList;
 import android.os.RemoteException;
 import android.os.ServiceManager;
@@ -43,11 +47,13 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.LocalePicker;
+import com.android.server.backup.Flags;
 import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager;
 
 import java.io.FileNotFoundException;
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Locale;
 import java.util.Set;
 
@@ -67,6 +73,13 @@
     private static final int LONG_PRESS_POWER_FOR_ASSISTANT = 5;
     /** See frameworks/base/core/res/res/values/config.xml#config_keyChordPowerVolumeUp **/
     private static final int KEY_CHORD_POWER_VOLUME_UP_GLOBAL_ACTIONS = 2;
+    @VisibleForTesting
+    static final String HIGH_CONTRAST_TEXT_RESTORED_BROADCAST_ACTION =
+            "com.android.settings.accessibility.ACTION_HIGH_CONTRAST_TEXT_RESTORED";
+
+    // Error messages for logging metrics.
+    private static final String ERROR_REMOTE_EXCEPTION_SETTING_LOCALE_DATA =
+        "remote_exception_setting_locale_data";
 
     private Context mContext;
     private AudioManager mAudioManager;
@@ -88,21 +101,26 @@
      */
     private static final ArraySet<String> sBroadcastOnRestore;
     private static final ArraySet<String> sBroadcastOnRestoreSystemUI;
+    private static final ArraySet<String> sBroadcastOnRestoreAccessibility;
     static {
-        sBroadcastOnRestore = new ArraySet<String>(12);
+        sBroadcastOnRestore = new ArraySet<>(7);
         sBroadcastOnRestore.add(Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
         sBroadcastOnRestore.add(Settings.Secure.ENABLED_VR_LISTENERS);
-        sBroadcastOnRestore.add(Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
         sBroadcastOnRestore.add(Settings.Global.BLUETOOTH_ON);
         sBroadcastOnRestore.add(Settings.Secure.UI_NIGHT_MODE);
         sBroadcastOnRestore.add(Settings.Secure.DARK_THEME_CUSTOM_START_TIME);
         sBroadcastOnRestore.add(Settings.Secure.DARK_THEME_CUSTOM_END_TIME);
-        sBroadcastOnRestore.add(Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED);
-        sBroadcastOnRestore.add(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS);
-        sBroadcastOnRestore.add(Settings.Secure.ACCESSIBILITY_QS_TARGETS);
-        sBroadcastOnRestore.add(Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE);
         sBroadcastOnRestore.add(Settings.Secure.SCREEN_RESOLUTION_MODE);
-        sBroadcastOnRestoreSystemUI = new ArraySet<String>(2);
+
+        sBroadcastOnRestoreAccessibility = new ArraySet<>(5);
+        sBroadcastOnRestoreAccessibility.add(Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
+        sBroadcastOnRestoreAccessibility.add(
+                Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED);
+        sBroadcastOnRestoreAccessibility.add(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS);
+        sBroadcastOnRestoreAccessibility.add(Settings.Secure.ACCESSIBILITY_QS_TARGETS);
+        sBroadcastOnRestoreAccessibility.add(Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE);
+
+        sBroadcastOnRestoreSystemUI = new ArraySet<>(2);
         sBroadcastOnRestoreSystemUI.add(Settings.Secure.QS_TILES);
         sBroadcastOnRestoreSystemUI.add(Settings.Secure.QS_AUTO_ADDED_TILES);
     }
@@ -175,6 +193,7 @@
         String oldValue = null;
         boolean sendBroadcast = false;
         boolean sendBroadcastSystemUI = false;
+        boolean sendBroadcastAccessibility = false;
         final SettingsLookup table;
 
         if (destination.equals(Settings.Secure.CONTENT_URI)) {
@@ -187,6 +206,7 @@
 
         sendBroadcast = sBroadcastOnRestore.contains(name);
         sendBroadcastSystemUI = sBroadcastOnRestoreSystemUI.contains(name);
+        sendBroadcastAccessibility = sBroadcastOnRestoreAccessibility.contains(name);
 
         if (sendBroadcast) {
             // TODO: http://b/22388012
@@ -196,6 +216,10 @@
             // It would probably be correct to do it for the ones sent to the system, but consumers
             // may be depending on the current behavior.
             oldValue = table.lookup(cr, name, context.getUserId());
+        } else if (sendBroadcastAccessibility) {
+            int userId = android.view.accessibility.Flags.restoreA11ySecureSettingsOnHsumDevice()
+                    ? context.getUserId() : UserHandle.USER_SYSTEM;
+            oldValue = table.lookup(cr, name, userId);
         }
 
         try {
@@ -238,14 +262,27 @@
             } else if (Settings.System.ACCELEROMETER_ROTATION.equals(name)
                     && shouldSkipAutoRotateRestore()) {
                 return;
-            } else if (Settings.Secure.ACCESSIBILITY_QS_TARGETS.equals(name)) {
+            } else if (shouldSkipAndLetBroadcastHandlesRestoreLogic(name)) {
                 // Don't write it to setting. Let the broadcast receiver in
                 // AccessibilityManagerService handle restore/merging logic.
                 return;
-            } else if (Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE.equals(name)) {
-                // Don't write it to setting. Let the broadcast receiver in
-                // AccessibilityManagerService handle restore/merging logic.
-                return;
+            } else if (com.android.graphics.hwui.flags.Flags.highContrastTextSmallTextRect()
+                    && Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED.equals(name)) {
+                final boolean currentlyEnabled = Settings.Secure.getInt(
+                        context.getContentResolver(),
+                        Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED, 0) == 1;
+                final boolean enabledInRestore = value != null && Integer.parseInt(value) == 1;
+
+                // If restoring from Android 15 or earlier and the user didn't already enable HCT
+                // on this new device, then don't restore and trigger custom migration logic.
+                final boolean needsCustomMigration = !currentlyEnabled
+                        && restoredFromSdkInt < Build.VERSION_CODES.BAKLAVA
+                        && enabledInRestore;
+                if (needsCustomMigration) {
+                    migrateHighContrastText(context);
+                    return;
+                }
+                // fall through to the ordinary write to settings
             }
 
             // Default case: write the restored value to settings
@@ -257,12 +294,13 @@
             // If we fail to apply the setting, by definition nothing happened
             sendBroadcast = false;
             sendBroadcastSystemUI = false;
+            sendBroadcastAccessibility = false;
             Log.e(TAG, "Failed to restore setting name: " + name + " + value: " + value, e);
         } finally {
             // If this was an element of interest, send the "we just restored it"
             // broadcast with the historical value now that the new value has
             // been committed and observers kicked off.
-            if (sendBroadcast || sendBroadcastSystemUI) {
+            if (sendBroadcast || sendBroadcastSystemUI || sendBroadcastAccessibility) {
                 Intent intent = new Intent(Intent.ACTION_SETTING_RESTORED)
                         .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY)
                         .putExtra(Intent.EXTRA_SETTING_NAME, name)
@@ -279,6 +317,13 @@
                             context.getString(com.android.internal.R.string.config_systemUi));
                     context.sendBroadcastAsUser(intent, context.getUser(), null);
                 }
+                if (sendBroadcastAccessibility) {
+                    UserHandle userHandle =
+                            android.view.accessibility.Flags.restoreA11ySecureSettingsOnHsumDevice()
+                                    ? context.getUser() : UserHandle.SYSTEM;
+                    intent.setPackage("android");
+                    context.sendBroadcastAsUser(intent, userHandle, null);
+                }
             }
         }
     }
@@ -444,6 +489,19 @@
         }
     }
 
+    private boolean shouldSkipAndLetBroadcastHandlesRestoreLogic(String settingName) {
+        boolean restoreHandledByBroadcast = Settings.Secure.ACCESSIBILITY_QS_TARGETS.equals(
+                settingName)
+                || Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE.equals(settingName);
+        if (android.view.accessibility.Flags.restoreA11ySecureSettingsOnHsumDevice()) {
+            restoreHandledByBroadcast |=
+                    Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS.equals(settingName)
+                            || Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES.equals(settingName);
+        }
+
+        return restoreHandledByBroadcast;
+    }
+
     private void setAutoRestore(boolean enabled) {
         try {
             IBackupManager bm = IBackupManager.Stub.asInterface(
@@ -523,11 +581,40 @@
         }
     }
 
+    private static void migrateHighContrastText(Context context) {
+        final Intent intent = new Intent(HIGH_CONTRAST_TEXT_RESTORED_BROADCAST_ACTION)
+                .setPackage(getSettingsAppPackage(context));
+        context.sendBroadcastAsUser(intent, context.getUser(), null);
+    }
+
+    /**
+     * Returns the System Settings application's package name
+     */
+    private static String getSettingsAppPackage(Context context) {
+        String settingsAppPackage = null;
+        PackageManager packageManager = context.getPackageManager();
+        if (packageManager != null) {
+            List<ResolveInfo> results = packageManager.queryIntentActivities(
+                    new Intent(Settings.ACTION_SETTINGS),
+                    PackageManager.MATCH_SYSTEM_ONLY);
+            if (!results.isEmpty()) {
+                settingsAppPackage = results.getFirst().activityInfo.applicationInfo.packageName;
+            }
+        }
+
+        return !TextUtils.isEmpty(settingsAppPackage) ? settingsAppPackage : "com.android.settings";
+    }
+
     /* package */ byte[] getLocaleData() {
         Configuration conf = mContext.getResources().getConfiguration();
         return conf.getLocales().toLanguageTags().getBytes();
     }
 
+    LocaleList getLocaleList() {
+        Configuration conf = mContext.getResources().getConfiguration();
+        return conf.getLocales();
+    }
+
     private static Locale toFullLocale(@NonNull Locale locale) {
         if (locale.getScript().isEmpty() || locale.getCountry().isEmpty()) {
             return ULocale.addLikelySubtags(ULocale.forLocale(locale)).toLocale();
@@ -653,8 +740,12 @@
      * code and {@code CC} is a two letter country code.
      *
      * @param data the comma separated BCP-47 language tags in bytes.
+     * @param size the size of the data in bytes.
+     * @param backupRestoreEventLogger the logger to log the restore event.
+     * @param dataType the data type of the setting for logging purposes.
      */
-    /* package */ void setLocaleData(byte[] data, int size) {
+    /* package */ void setLocaleData(
+        byte[] data, int size, BackupRestoreEventLogger backupRestoreEventLogger, String dataType) {
         final Configuration conf = mContext.getResources().getConfiguration();
 
         // Replace "_" with "-" to deal with older backups.
@@ -681,8 +772,18 @@
 
             am.updatePersistentConfigurationWithAttribution(config, mContext.getOpPackageName(),
                     mContext.getAttributionTag());
+            if (Flags.enableMetricsSettingsBackupAgents()) {
+                backupRestoreEventLogger
+                    .logItemsRestored(dataType, localeList.size());
+            }
         } catch (RemoteException e) {
-            // Intentionally left blank
+            if (Flags.enableMetricsSettingsBackupAgents()) {
+                backupRestoreEventLogger
+                    .logItemsRestoreFailed(
+                        dataType,
+                        localeList.size(),
+                        ERROR_REMOTE_EXCEPTION_SETTING_LOCALE_DATA);
+            }
         }
     }
 
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 37eda3e..f9c6442 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -1778,6 +1778,9 @@
                 Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED,
                 SecureSettingsProto.Accessibility.HIGH_TEXT_CONTRAST_ENABLED);
         dumpSetting(s, p,
+                Settings.Secure.ACCESSIBILITY_HCT_RECT_PROMPT_STATUS,
+                SecureSettingsProto.Accessibility.HCT_RECT_PROMPT_STATUS);
+        dumpSetting(s, p,
                 Settings.Secure.CONTRAST_LEVEL,
                 SecureSettingsProto.Accessibility.CONTRAST_LEVEL);
         dumpSetting(s, p,
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 6128d45..55f48e3 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -2089,7 +2089,33 @@
                 // setting.
                 return false;
             }
-            final String mimeType = getContext().getContentResolver().getType(audioUri);
+
+            // If the audioUri comes from FileProvider, the security check will fail. Currently, it
+            // should not have too many FileProvider Uri usage, using a workaround fix here.
+            // Only allow for caller is privileged apps
+            ApplicationInfo aInfo = null;
+            try {
+                aInfo = getCallingApplicationInfoOrThrow();
+            } catch (IllegalStateException ignored) {
+                Slog.w(LOG_TAG, "isValidMediaUri: cannot get calling app info for setting: "
+                        + name + " URI: " + audioUri);
+                return false;
+            }
+            final boolean isPrivilegedApp = aInfo != null ? aInfo.isPrivilegedApp() : false;
+            String mimeType = null;
+            if (isPrivilegedApp) {
+                final long identity = Binder.clearCallingIdentity();
+                try {
+                    mimeType = getContext().getContentResolver().getType(audioUri);
+                } finally {
+                    Binder.restoreCallingIdentity(identity);
+                }
+            } else {
+                mimeType = getContext().getContentResolver().getType(audioUri);
+            }
+            if (DEBUG) {
+                Slog.v(LOG_TAG, "isValidMediaUri mimeType: " + mimeType);
+            }
             if (mimeType == null) {
                 Slog.e(LOG_TAG,
                         "mutateSystemSetting for setting: " + name + " URI: " + audioUri
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 9004488..c88a7fd 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -182,6 +182,7 @@
                     Settings.Global.DEVELOPMENT_FORCE_RTL,
                     Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW,
                     Settings.Global.DEVELOPMENT_RENDER_SHADOWS_IN_COMPOSITOR,
+                    Settings.Global.DEVELOPMENT_SHADE_DISPLAY_AWARENESS,
                     Settings.Global.DEVELOPMENT_WM_DISPLAY_SETTINGS_PATH,
                     Settings.Global.DEVICE_DEMO_MODE,
                     Settings.Global.DEVICE_IDLE_CONSTANTS,
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperRestoreTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperRestoreTest.java
index 048d93b..62c03dd 100644
--- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperRestoreTest.java
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperRestoreTest.java
@@ -26,18 +26,22 @@
 import android.content.Intent;
 import android.net.Uri;
 import android.os.Build;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
 import android.provider.Settings;
 import android.provider.SettingsStringUtil;
+import android.view.accessibility.Flags;
 
-import androidx.test.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
 
 import com.android.internal.util.test.BroadcastInterceptingContext;
 
+import org.junit.After;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.Mockito;
 
 import java.util.concurrent.ExecutionException;
 
@@ -48,18 +52,100 @@
  */
 @RunWith(AndroidJUnit4.class)
 public class SettingsHelperRestoreTest {
-
+    @Rule
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+    public final BroadcastInterceptingContext mInterceptingContext =
+            new BroadcastInterceptingContext(
+                    InstrumentationRegistry.getInstrumentation().getContext());
     private static final float FLOAT_TOLERANCE = 0.01f;
-
-    private Context mContext;
     private ContentResolver mContentResolver;
     private SettingsHelper mSettingsHelper;
 
     @Before
     public void setUp() {
-        mContext = InstrumentationRegistry.getContext();
-        mContentResolver = mContext.getContentResolver();
-        mSettingsHelper = new SettingsHelper(mContext);
+        mContentResolver = mInterceptingContext.getContentResolver();
+        mSettingsHelper = new SettingsHelper(mInterceptingContext);
+    }
+
+    @After
+    public void cleanUp() {
+        setDefaultAccessibilityDisplayMagnificationScale();
+        Settings.Secure.putInt(mContentResolver,
+                Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED, 0);
+        Settings.Secure.putString(mContentResolver, Settings.Secure.ACCESSIBILITY_QS_TARGETS, null);
+        Settings.Secure.putString(mContentResolver,
+                Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, null);
+        Settings.Secure.putString(mContentResolver,
+                Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, null);
+    }
+
+    @Test
+    public void restoreHighTextContrastEnabled_currentlyEnabled_enableInRestoredFromVanilla_dontSendNotification_hctKeepsEnabled()
+            throws ExecutionException, InterruptedException {
+        BroadcastInterceptingContext.FutureIntent futureIntent =
+                mInterceptingContext.nextBroadcastIntent(
+                        SettingsHelper.HIGH_CONTRAST_TEXT_RESTORED_BROADCAST_ACTION);
+        String settingName = Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED;
+        Settings.Secure.putInt(mContentResolver, settingName, 1);
+
+        mSettingsHelper.restoreValue(
+                mInterceptingContext,
+                mContentResolver,
+                new ContentValues(2),
+                Settings.Secure.getUriFor(settingName),
+                settingName,
+                String.valueOf(1),
+                Build.VERSION_CODES.VANILLA_ICE_CREAM);
+
+        futureIntent.assertNotReceived();
+        assertThat(Settings.Secure.getInt(mContentResolver, settingName, 0)).isEqualTo(1);
+    }
+
+    @EnableFlags(com.android.graphics.hwui.flags.Flags.FLAG_HIGH_CONTRAST_TEXT_SMALL_TEXT_RECT)
+    @Test
+    public void restoreHighTextContrastEnabled_currentlyDisabled_enableInRestoredFromVanilla_sendNotification_hctKeepsDisabled()
+            throws ExecutionException, InterruptedException {
+        BroadcastInterceptingContext.FutureIntent futureIntent =
+                mInterceptingContext.nextBroadcastIntent(
+                        SettingsHelper.HIGH_CONTRAST_TEXT_RESTORED_BROADCAST_ACTION);
+        String settingName = Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED;
+        Settings.Secure.putInt(mContentResolver, settingName, 0);
+
+        mSettingsHelper.restoreValue(
+                mInterceptingContext,
+                mContentResolver,
+                new ContentValues(2),
+                Settings.Secure.getUriFor(settingName),
+                settingName,
+                String.valueOf(1),
+                Build.VERSION_CODES.VANILLA_ICE_CREAM);
+
+        Intent intentReceived = futureIntent.get();
+        assertThat(intentReceived).isNotNull();
+        assertThat(intentReceived.getPackage()).isNotNull();
+        assertThat(Settings.Secure.getInt(mContentResolver, settingName, 0)).isEqualTo(0);
+    }
+
+    @Test
+    public void restoreHighTextContrastEnabled_currentlyDisabled_enableInRestoredFromAfterVanilla_dontSendNotification_hctShouldEnabled()
+            throws ExecutionException, InterruptedException {
+        BroadcastInterceptingContext.FutureIntent futureIntent =
+                mInterceptingContext.nextBroadcastIntent(
+                        SettingsHelper.HIGH_CONTRAST_TEXT_RESTORED_BROADCAST_ACTION);
+        String settingName = Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED;
+        Settings.Secure.putInt(mContentResolver, settingName, 0);
+
+        mSettingsHelper.restoreValue(
+                mInterceptingContext,
+                mContentResolver,
+                new ContentValues(2),
+                Settings.Secure.getUriFor(settingName),
+                settingName,
+                String.valueOf(1),
+                Build.VERSION_CODES.BAKLAVA);
+
+        futureIntent.assertNotReceived();
+        assertThat(Settings.Secure.getInt(mContentResolver, settingName, 0)).isEqualTo(1);
     }
 
     /** Tests for {@link Settings.Secure#ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE}. */
@@ -76,7 +162,7 @@
         Settings.Secure.putFloat(mContentResolver, settingName, configuredSettingValue);
 
         mSettingsHelper.restoreValue(
-                mContext,
+                mInterceptingContext,
                 mContentResolver,
                 new ContentValues(2),
                 Settings.Secure.getUriFor(settingName),
@@ -99,7 +185,7 @@
         float restoreSettingValue = defaultSettingValue + 0.5f;
 
         mSettingsHelper.restoreValue(
-                Mockito.mock(Context.class),
+                mInterceptingContext,
                 mContentResolver,
                 new ContentValues(2),
                 Settings.Secure.getUriFor(settingName),
@@ -121,7 +207,7 @@
      */
     private float setDefaultAccessibilityDisplayMagnificationScale() {
         float defaultSettingValue =
-                mContext.getResources()
+                mInterceptingContext.getResources()
                         .getFraction(
                                 R.fraction.def_accessibility_display_magnification_scale, 1, 1);
         Settings.Secure.putFloat(
@@ -142,7 +228,7 @@
         Settings.Secure.putInt(mContentResolver, settingName, configuredSettingValue);
 
         mSettingsHelper.restoreValue(
-                Mockito.mock(Context.class),
+                mInterceptingContext,
                 mContentResolver,
                 new ContentValues(2),
                 Settings.Secure.getUriFor(settingName),
@@ -164,7 +250,7 @@
 
         int restoreSettingValue = 1;
         mSettingsHelper.restoreValue(
-                Mockito.mock(Context.class),
+                mInterceptingContext,
                 mContentResolver,
                 new ContentValues(2),
                 Settings.Secure.getUriFor(settingName),
@@ -178,17 +264,15 @@
     @Test
     public void restoreAccessibilityQsTargets_broadcastSent()
             throws ExecutionException, InterruptedException {
-        BroadcastInterceptingContext interceptingContext = new BroadcastInterceptingContext(
-                mContext);
         final String settingName = Settings.Secure.ACCESSIBILITY_QS_TARGETS;
         final String restoreSettingValue = "com.android.server.accessibility/ColorInversion"
                 + SettingsStringUtil.DELIMITER
                 + "com.android.server.accessibility/ColorCorrectionTile";
         BroadcastInterceptingContext.FutureIntent futureIntent =
-                interceptingContext.nextBroadcastIntent(Intent.ACTION_SETTING_RESTORED);
+                mInterceptingContext.nextBroadcastIntent(Intent.ACTION_SETTING_RESTORED);
 
         mSettingsHelper.restoreValue(
-                interceptingContext,
+                mInterceptingContext,
                 mContentResolver,
                 new ContentValues(2),
                 Settings.Secure.getUriFor(settingName),
@@ -207,15 +291,13 @@
     @Test
     public void restoreAccessibilityShortcutTargetService_broadcastSent()
             throws ExecutionException, InterruptedException {
-        BroadcastInterceptingContext interceptingContext = new BroadcastInterceptingContext(
-                mContext);
         final String settingName = Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE;
         final String restoredValue = "com.android.a11y/Service";
         BroadcastInterceptingContext.FutureIntent futureIntent =
-                interceptingContext.nextBroadcastIntent(Intent.ACTION_SETTING_RESTORED);
+                mInterceptingContext.nextBroadcastIntent(Intent.ACTION_SETTING_RESTORED);
 
         mSettingsHelper.restoreValue(
-                interceptingContext,
+                mInterceptingContext,
                 mContentResolver,
                 new ContentValues(2),
                 Settings.Secure.getUriFor(settingName),
@@ -230,4 +312,32 @@
                 Intent.EXTRA_SETTING_RESTORED_FROM_SDK_INT, /* defaultValue= */ 0))
                 .isEqualTo(Build.VERSION.SDK_INT);
     }
+
+    @EnableFlags(Flags.FLAG_RESTORE_A11Y_SECURE_SETTINGS_ON_HSUM_DEVICE)
+    @Test
+    public void restoreAccessibilityShortcutTargets_broadcastSent()
+            throws ExecutionException, InterruptedException {
+        final String settingName = Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS;
+        final String restoreSettingValue = "com.android.server.accessibility/ColorInversion"
+                + SettingsStringUtil.DELIMITER
+                + "com.android.server.accessibility/ColorCorrectionTile";
+        BroadcastInterceptingContext.FutureIntent futureIntent =
+                mInterceptingContext.nextBroadcastIntent(Intent.ACTION_SETTING_RESTORED);
+
+        mSettingsHelper.restoreValue(
+                mInterceptingContext,
+                mContentResolver,
+                new ContentValues(2),
+                Settings.Secure.getUriFor(settingName),
+                settingName,
+                restoreSettingValue,
+                Build.VERSION.SDK_INT);
+
+        Intent intentReceived = futureIntent.get();
+        assertThat(intentReceived.getStringExtra(Intent.EXTRA_SETTING_NEW_VALUE))
+                .isEqualTo(restoreSettingValue);
+        assertThat(intentReceived.getIntExtra(
+                Intent.EXTRA_SETTING_RESTORED_FROM_SDK_INT, /* defaultValue= */ 0))
+                .isEqualTo(Build.VERSION.SDK_INT);
+    }
 }
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperTest.java
index cea2bbc..58200d4 100644
--- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperTest.java
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperTest.java
@@ -33,6 +33,7 @@
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.res.AssetFileDescriptor;
+import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.database.Cursor;
 import android.database.MatrixCursor;
@@ -83,6 +84,9 @@
             "content://media/internal/audio/media/30?title=DefaultAlarm&canonical=1";
     private static final String VIBRATION_FILE_NAME = "haptics.xml";
 
+    private static final LocaleList LOCALE_LIST =
+        LocaleList.forLanguageTags("en-US,en-UK");
+
     private SettingsHelper mSettingsHelper;
 
     @Rule
@@ -778,6 +782,15 @@
         assertThat(getAutoRotationSettingValue()).isEqualTo(previousValue);
     }
 
+    @Test
+    public void getLocaleList_returnsLocaleList() {
+        Configuration config = new Configuration();
+        config.setLocales(LOCALE_LIST);
+        when(mResources.getConfiguration()).thenReturn(config);
+
+        assertThat(mSettingsHelper.getLocaleList()).isEqualTo(LOCALE_LIST);
+    }
+
     private int getAutoRotationSettingValue() {
         return Settings.System.getInt(mContentResolver,
                 Settings.System.ACCELEROMETER_ROTATION,
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 65f4877..fb4293a 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -348,6 +348,7 @@
     <uses-permission android:name="android.permission.REQUEST_COMPANION_PROFILE_COMPUTER" />
     <uses-permission android:name="android.permission.REQUEST_COMPANION_PROFILE_AUTOMOTIVE_PROJECTION" />
     <uses-permission android:name="android.permission.REQUEST_COMPANION_PROFILE_NEARBY_DEVICE_STREAMING" />
+    <uses-permission android:name="android.permission.REQUEST_COMPANION_PROFILE_SENSOR_DEVICE_STREAMING" />
     <uses-permission android:name="android.permission.REQUEST_COMPANION_PROFILE_WATCH" />
     <uses-permission android:name="android.permission.REQUEST_COMPANION_PROFILE_GLASSES" />
     <uses-permission android:name="android.permission.REQUEST_COMPANION_SELF_MANAGED" />
diff --git a/packages/Shell/OWNERS b/packages/Shell/OWNERS
index d4b5b862..576afdc 100644
--- a/packages/Shell/OWNERS
+++ b/packages/Shell/OWNERS
@@ -13,3 +13,7 @@
 omakoto@google.com
 michaelwr@google.com
 ronish@google.com
+
+# Wear Bugreport Owners
+ranamouawi@google.com
+yashasvig@google.com
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index 0694b61..c655504 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -257,6 +257,9 @@
     /** Always keep remote bugreport files created in the last day. */
     private static final long REMOTE_MIN_KEEP_AGE = DateUtils.DAY_IN_MILLIS;
 
+    /** Minimum delay for sending last update notification */
+    private static final int DELAY_NOTIFICATION_MS = 250;
+
     private final Object mLock = new Object();
 
     /** Managed bugreport info (keyed by id) */
@@ -927,6 +930,7 @@
             Log.d(TAG, "Progress #" + info.id + ": " + percentageText);
         }
         info.lastProgress.set(progress);
+        info.lastUpdate.set(System.currentTimeMillis());
 
         sendForegroundabledNotification(info.id, builder.build());
     }
@@ -1455,6 +1459,16 @@
      */
     private void sendBugreportNotification(BugreportInfo info, boolean takingScreenshot) {
 
+        final long lastUpdate = System.currentTimeMillis() - info.lastUpdate.longValue();
+        if (lastUpdate < DELAY_NOTIFICATION_MS) {
+            Log.d(TAG, "Delaying final notification for "
+                    + (DELAY_NOTIFICATION_MS - lastUpdate) + " ms ");
+            mMainThreadHandler.postDelayed(() -> {
+                sendBugreportNotification(info, takingScreenshot);
+            }, DELAY_NOTIFICATION_MS - lastUpdate);
+            return;
+        }
+
         // Since adding the details can take a while, do it before notifying user.
         addDetailsToZipFile(info);
 
@@ -1475,6 +1489,7 @@
         final Notification.Builder builder = newBaseNotification(mContext)
                 .setContentTitle(title)
                 .setTicker(title)
+                .setProgress(100 /* max value of progress percentage */, 100, false)
                 .setOnlyAlertOnce(false)
                 .setContentText(content);
 
@@ -2743,7 +2758,6 @@
             }
         }
         info.progress.set(progress);
-        info.lastUpdate.set(System.currentTimeMillis());
 
         updateProgress(info);
     }
diff --git a/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java b/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java
index 050a370..7bda2ea 100644
--- a/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java
+++ b/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java
@@ -193,7 +193,7 @@
         mService.mScreenshotDelaySec = SCREENSHOT_DELAY_SECONDS;
         // Dup the fds which are passing to startBugreport function.
         Mockito.doAnswer(invocation -> {
-            final boolean isScreenshotRequested = invocation.getArgument(6);
+            final boolean isScreenshotRequested = invocation.getArgument(7);
             if (isScreenshotRequested) {
                 mScreenshotFd = ParcelFileDescriptor.dup(invocation.getArgument(3));
             }
@@ -250,7 +250,22 @@
         mIDumpstateListener.onProgress(300);
         assertProgressNotification(mProgressTitle, 99);
 
-        Bundle extras = sendBugreportFinishedAndGetSharedIntent(mBugreportId);
+        Bundle extras = sendBugreportFinishedAndGetSharedIntent(mBugreportId, 1);
+        assertActionSendMultiple(extras);
+
+        assertServiceNotRunning();
+    }
+
+    @Test
+    public void testStressProgress() throws Exception {
+        sendBugreportStarted();
+        waitForScreenshotButtonEnabled(true);
+
+        for (int i = 0; i <= 1000; i++) {
+            mIDumpstateListener.onProgress(i);
+        }
+        sendBugreportFinished();
+        Bundle extras = acceptBugreportAndGetSharedIntent(mBugreportId, 1);
         assertActionSendMultiple(extras);
 
         assertServiceNotRunning();
@@ -277,7 +292,7 @@
         assertScreenshotButtonEnabled(false);
         waitForScreenshotButtonEnabled(true);
 
-        Bundle extras = sendBugreportFinishedAndGetSharedIntent(mBugreportId);
+        Bundle extras = sendBugreportFinishedAndGetSharedIntent(mBugreportId, 2);
         assertActionSendMultiple(extras, NO_NAME, NO_TITLE, NO_DESCRIPTION, 1);
 
         assertServiceNotRunning();
@@ -294,7 +309,7 @@
         // There's no indication in the UI about the screenshot finish, so just sleep like a baby...
         sleep(SAFE_SCREENSHOT_DELAY * DateUtils.SECOND_IN_MILLIS);
 
-        Bundle extras = acceptBugreportAndGetSharedIntent(mBugreportId);
+        Bundle extras = acceptBugreportAndGetSharedIntent(mBugreportId, 2);
         assertActionSendMultiple(extras, NO_NAME, NO_TITLE, NO_DESCRIPTION, 1);
 
         assertServiceNotRunning();
@@ -328,7 +343,7 @@
 
         assertProgressNotification(NEW_NAME, 00.00f);
 
-        Bundle extras = sendBugreportFinishedAndGetSharedIntent(TITLE);
+        Bundle extras = sendBugreportFinishedAndGetSharedIntent(TITLE, 1);
         assertActionSendMultiple(extras, NEW_NAME, TITLE, mDescription, 0);
 
         assertServiceNotRunning();
@@ -363,7 +378,7 @@
 
         assertProgressNotification(NEW_NAME, 00.00f);
 
-        Bundle extras = sendBugreportFinishedAndGetSharedIntent(TITLE);
+        Bundle extras = sendBugreportFinishedAndGetSharedIntent(TITLE, 1);
         assertActionSendMultiple(extras, NEW_NAME, TITLE, mDescription, 0);
 
         assertServiceNotRunning();
@@ -390,7 +405,7 @@
         detailsUi.descField.setText(mDescription);
         detailsUi.clickOk();
 
-        Bundle extras = sendBugreportFinishedAndGetSharedIntent(mBugreportId);
+        Bundle extras = sendBugreportFinishedAndGetSharedIntent(mBugreportId, 1);
         assertActionSendMultiple(extras, NO_NAME, NO_TITLE, mDescription, 0);
 
         assertServiceNotRunning();
@@ -441,7 +456,7 @@
         detailsUi.clickOk();
 
         // Finally, share bugreport.
-        Bundle extras = acceptBugreportAndGetSharedIntent(mBugreportId);
+        Bundle extras = acceptBugreportAndGetSharedIntent(mBugreportId, 1);
         assertActionSendMultiple(extras, NO_NAME, TITLE, mDescription, 0);
 
         assertServiceNotRunning();
@@ -504,7 +519,7 @@
         mUiBot.click(ok, "ok");
 
         // Share the bugreport.
-        mUiBot.chooseActivity(UI_NAME);
+        mUiBot.chooseActivity(UI_NAME, mContext, 1);
         Bundle extras = mListener.getExtras();
         assertActionSendMultiple(extras);
 
@@ -531,7 +546,7 @@
         sendBugreportFinished();
         killService();
         assertServiceNotRunning();
-        Bundle extras = acceptBugreportAndGetSharedIntent(mBugreportId);
+        Bundle extras = acceptBugreportAndGetSharedIntent(mBugreportId, 1);
         assertActionSendMultiple(extras);
     }
 
@@ -618,45 +633,49 @@
      * Sends a "bugreport finished" event and waits for the result.
      *
      * @param id The bugreport id for finished notification string title substitution.
+     * @param count Number of files to be shared
      * @return extras sent in the shared intent.
      */
-    private Bundle sendBugreportFinishedAndGetSharedIntent(int id) throws Exception {
+    private Bundle sendBugreportFinishedAndGetSharedIntent(int id, int count) throws Exception {
         sendBugreportFinished();
-        return acceptBugreportAndGetSharedIntent(id);
+        return acceptBugreportAndGetSharedIntent(id, count);
     }
 
     /**
      * Sends a "bugreport finished" event and waits for the result.
      *
      * @param notificationTitle The title of finished notification.
+     * @param count Number of files to be shared
      * @return extras sent in the shared intent.
      */
-    private Bundle sendBugreportFinishedAndGetSharedIntent(String notificationTitle)
+    private Bundle sendBugreportFinishedAndGetSharedIntent(String notificationTitle, int count)
             throws Exception {
         sendBugreportFinished();
-        return acceptBugreportAndGetSharedIntent(notificationTitle);
+        return acceptBugreportAndGetSharedIntent(notificationTitle, count);
     }
 
     /**
      * Accepts the notification to share the finished bugreport and waits for the result.
      *
      * @param id The bugreport id for finished notification string title substitution.
+     * @param count Number of files to be shared
      * @return extras sent in the shared intent.
      */
-    private Bundle acceptBugreportAndGetSharedIntent(int id) {
+    private Bundle acceptBugreportAndGetSharedIntent(int id, int count) {
         final String notificationTitle = mContext.getString(R.string.bugreport_finished_title, id);
-        return acceptBugreportAndGetSharedIntent(notificationTitle);
+        return acceptBugreportAndGetSharedIntent(notificationTitle, count);
     }
 
     /**
      * Accepts the notification to share the finished bugreport and waits for the result.
      *
      * @param notificationTitle The title of finished notification.
+     * @param count Number of files to be shared
      * @return extras sent in the shared intent.
      */
-    private Bundle acceptBugreportAndGetSharedIntent(String notificationTitle) {
+    private Bundle acceptBugreportAndGetSharedIntent(String notificationTitle, int count) {
         mUiBot.clickOnNotification(notificationTitle);
-        mUiBot.chooseActivity(UI_NAME);
+        mUiBot.chooseActivity(UI_NAME, mContext, count);
         return mListener.getExtras();
     }
 
diff --git a/packages/Shell/tests/src/com/android/shell/UiBot.java b/packages/Shell/tests/src/com/android/shell/UiBot.java
index ce9f70d..60008a3 100644
--- a/packages/Shell/tests/src/com/android/shell/UiBot.java
+++ b/packages/Shell/tests/src/com/android/shell/UiBot.java
@@ -18,9 +18,12 @@
 
 import android.app.Instrumentation;
 import android.app.StatusBarManager;
+import android.content.Context;
+import android.content.res.Resources;
 import android.os.SystemClock;
 import android.text.format.DateUtils;
 import android.util.Log;
+import android.util.PluralsMessageFormatter;
 
 import androidx.test.uiautomator.By;
 import androidx.test.uiautomator.UiDevice;
@@ -34,7 +37,9 @@
 import static junit.framework.Assert.assertNotNull;
 import static junit.framework.Assert.assertTrue;
 
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 /**
  * A helper class for UI-related testing tasks.
@@ -206,11 +211,26 @@
      *
      * @param name name of the activity as displayed in the UI (typically the value set by
      *            {@code android:label} in the manifest).
+     * @param context Context of the target application
+     * @param count Number of files to be shared
      */
-    public void chooseActivity(String name) {
+    public void chooseActivity(String name, Context context, int count) {
         // It uses an intent chooser now, so just getting the activity by text is enough...
-        final String share = mInstrumentation.getContext().getString(
-                com.android.internal.R.string.share);
+        Resources res = null;
+        try {
+            res = context.getPackageManager()
+                    .getResourcesForApplication("com.android.intentresolver");
+        } catch (Exception e)  {
+            assertNotNull("could not get resources for com.android.intentresolver", res);
+        }
+        /* Resource read is defined as a string which contains a plural
+         * which needs some formatting */
+        Map<String, Object> arguments = new HashMap<>();
+        arguments.put("count", count);
+        final String share = PluralsMessageFormatter.format(
+                res,
+                arguments,
+                res.getIdentifier("sharing_files", "string", "com.android.intentresolver"));
         boolean gotIt = mDevice.wait(Until.hasObject(By.text(share)), mTimeout);
         assertTrue("could not get share activity (" + share + ")", gotIt);
         swipeUp();
diff --git a/packages/SimAppDialog/res/values-af/strings.xml b/packages/SimAppDialog/res/values-af/strings.xml
index 143bc67..7a2ed82 100644
--- a/packages/SimAppDialog/res/values-af/strings.xml
+++ b/packages/SimAppDialog/res/values-af/strings.xml
@@ -19,8 +19,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="8898068901680117589">"SIM-programdialoog"</string>
     <string name="install_carrier_app_title" msgid="334729104862562585">"Aktiveer mobiele diens"</string>
-    <string name="install_carrier_app_description" msgid="4014303558674923797">"Jy sal die <xliff:g id="ID_1">%1$s</xliff:g>-program moet installeer om jou nuwe SIM behoorlik te laat werk"</string>
+    <string name="install_carrier_app_description" msgid="4014303558674923797">"Jy sal die <xliff:g id="ID_1">%1$s</xliff:g>-app moet installeer om jou nuwe SIM behoorlik te laat werk"</string>
     <string name="install_carrier_app_description_default" msgid="7356830245205847840">"Jy sal die diensverskafferprogram moet installeer om jou nuwe SIM behoorlik te laat werk"</string>
     <string name="install_carrier_app_defer_action" msgid="2558576736886876209">"Nie nou nie"</string>
-    <string name="install_carrier_app_download_action" msgid="7859229305958538064">"Laai program af"</string>
+    <string name="install_carrier_app_download_action" msgid="7859229305958538064">"Laai app af"</string>
 </resources>
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 11cb070..31d5bfa 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -322,6 +322,9 @@
     <!-- Query all packages on device on R+ -->
     <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
 
+    <!-- Query advanced protection state -->
+    <uses-permission android:name="android.permission.QUERY_ADVANCED_PROTECTION_MODE" />
+
     <queries>
         <intent>
             <action android:name="android.intent.action.CREATE_NOTE" />
diff --git a/packages/SystemUI/aconfig/biometrics_framework.aconfig b/packages/SystemUI/aconfig/biometrics_framework.aconfig
index e3f5378..9692aa5 100644
--- a/packages/SystemUI/aconfig/biometrics_framework.aconfig
+++ b/packages/SystemUI/aconfig/biometrics_framework.aconfig
@@ -4,16 +4,6 @@
 # NOTE: Keep alphabetized to help limit merge conflicts from multiple simultaneous editors.
 
 flag {
-  name: "bp_icon_a11y"
-  namespace: "biometrics_framework"
-  description: "Fixes biometric prompt icon not working as button with a11y"
-  bug: "359423579"
-  metadata {
-    purpose: PURPOSE_BUGFIX
-  }
-}
-
-flag {
     name: "cont_auth_plugin"
     namespace: "biometrics_framework"
     description: "Plugin and related API hooks for contextual auth plugins"
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index ee22915..1b1c91d 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -265,14 +265,6 @@
 }
 
 flag {
-    name: "keyguard_bottom_area_refactor"
-    namespace: "systemui"
-    description: "Bottom area of keyguard refactor move into KeyguardRootView. Includes "
-        "lock icon and others."
-    bug: "290652751"
-}
-
-flag {
     name: "device_entry_udfps_refactor"
     namespace: "systemui"
     description: "Refactoring device entry UDFPS icon to use modern architecture and "
@@ -504,14 +496,14 @@
 }
 
 flag {
-    name: "status_bar_notification_chips_test"
+    name: "promote_notifications_automatically"
     namespace: "systemui"
-    description: "Flag to enable certain features that let us test the status bar notification "
-        "chips with teamfooders. This flag should *never* be released to trunkfood or nextfood."
-    bug: "361346412"
+    description: "Flag to automatically turn certain notifications into promoted notifications so "
+        " we can test promoted notifications with teamfooders. This flag should *never* be released "
+        "to trunkfood or nextfood."
+    bug: "367705002"
 }
 
-
 flag {
     name: "compose_bouncer"
     namespace: "systemui"
@@ -679,7 +671,7 @@
 flag {
     name: "volume_redesign"
     namespace: "systemui"
-    description: "Enables Volume BC25 visuals update"
+    description: "Enables Volume visuals update"
     bug: "368308908"
 }
 
@@ -1325,7 +1317,7 @@
   name: "media_controls_ui_update"
   namespace: "systemui"
   description: "Enables media visuals update"
-  bug: "380053768"
+  bug: "379044958"
 }
 
 flag {
@@ -1339,16 +1331,6 @@
 }
 
 flag {
-  name: "validate_keyboard_shortcut_helper_icon_uri"
-  namespace: "systemui"
-  description: "Adds a check that the caller can access the content URI of an icon in the shortcut helper."
-  bug: "331180422"
-  metadata {
-    purpose: PURPOSE_BUGFIX
-  }
-}
-
-flag {
   name: "glanceable_hub_allow_keyguard_when_dreaming"
   namespace: "systemui"
   description: "Allows users to exit dream to keyguard with glanceable hub enabled"
@@ -1879,3 +1861,24 @@
     description: "Larger privacy indicators on large screen"
     bug: "381864715"
 }
+
+flag {
+    name: "desktop_effects_qs_tile"
+    namespace: "systemui"
+    description: "Enables the QS tile for desktop effects"
+    bug: "376797327"
+}
+
+flag {
+   name: "hub_edit_mode_touch_adjustments"
+   namespace: "systemui"
+   description: "Makes selected widget toggleable in edit mode and modifier buttons mutually exclusive."
+   bug: "383160667"
+}
+
+flag {
+   name: "glanceable_hub_direct_edit_mode"
+   namespace: "systemui"
+   description: "Invokes edit mode directly from long press in glanceable hub"
+   bug: "382531177"
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
index 85f549d..55b4293 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
@@ -19,6 +19,7 @@
 import androidx.compose.foundation.Canvas
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.overscroll
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.DisposableEffect
@@ -101,6 +102,7 @@
             viewModel,
             dialogFactory,
             Modifier.element(Bouncer.Elements.Content)
+                .overscroll(verticalOverscrollEffect)
                 .sysuiResTag(Bouncer.TestTags.Root)
                 .fillMaxSize(),
         )
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
index a55b6d7..5dbedc7 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
@@ -43,6 +43,8 @@
 import androidx.compose.foundation.focusable
 import androidx.compose.foundation.gestures.awaitFirstDown
 import androidx.compose.foundation.gestures.detectHorizontalDragGestures
+import androidx.compose.foundation.gestures.snapping.SnapPosition
+import androidx.compose.foundation.gestures.snapping.rememberSnapFlingBehavior
 import androidx.compose.foundation.interaction.MutableInteractionSource
 import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Box
@@ -151,6 +153,7 @@
 import androidx.compose.ui.unit.Density
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.DpSize
+import androidx.compose.ui.unit.IntOffset
 import androidx.compose.ui.unit.IntRect
 import androidx.compose.ui.unit.IntSize
 import androidx.compose.ui.unit.LayoutDirection
@@ -293,9 +296,19 @@
                                     offset.y,
                                 ) - contentOffset
                             val index = firstIndexAtOffset(gridState, adjustedOffset)
-                            val key =
+                            val tappedKey =
                                 index?.let { keyAtIndexIfEditable(contentListState.list, index) }
-                            viewModel.setSelectedKey(key)
+
+                            viewModel.setSelectedKey(
+                                if (
+                                    Flags.hubEditModeTouchAdjustments() &&
+                                        selectedKey.value == tappedKey
+                                ) {
+                                    null
+                                } else {
+                                    tappedKey
+                                }
+                            )
                         }
                     }
                 }
@@ -749,10 +762,13 @@
     content: LazyGridScope.(sizeInfo: SizeInfo?) -> Unit,
 ) {
     if (communalResponsiveGrid()) {
+        val flingBehavior =
+            rememberSnapFlingBehavior(lazyGridState = gridState, snapPosition = SnapPosition.Start)
         ResponsiveLazyHorizontalGrid(
             cellAspectRatio = 1.5f,
             modifier = modifier,
             state = gridState,
+            flingBehavior = flingBehavior,
             minContentPadding = minContentPadding,
             minHorizontalArrangement = Dimensions.ItemSpacing,
             minVerticalArrangement = Dimensions.ItemSpacing,
@@ -909,7 +925,7 @@
                         Arrangement.spacedBy(
                             sizeInfo?.verticalArrangement ?: Dimensions.ItemSpacing
                         ),
-                    enabled = selected,
+                    enabled = selected && !isItemDragging,
                     alpha = { outlineAlpha },
                     modifier =
                         Modifier.requiredSize(dpSize)
@@ -947,12 +963,28 @@
                     }
                 }
             } else {
+                val itemAlpha =
+                    if (communalResponsiveGrid()) {
+                        val percentVisible by
+                            remember(gridState, index) {
+                                derivedStateOf { calculatePercentVisible(gridState, index) }
+                            }
+                        animateFloatAsState(percentVisible)
+                    } else {
+                        null
+                    }
+
                 CommunalContent(
                     model = item,
                     viewModel = viewModel,
                     size = size,
                     selected = false,
-                    modifier = Modifier.requiredSize(dpSize).animateItem(),
+                    modifier =
+                        Modifier.requiredSize(dpSize).animateItem().thenIf(
+                            communalResponsiveGrid()
+                        ) {
+                            Modifier.graphicsLayer { alpha = itemAlpha?.value ?: 1f }
+                        },
                     index = index,
                     contentListState = contentListState,
                     interactionHandler = interactionHandler,
@@ -988,7 +1020,7 @@
                 text = titleForEmptyStateCTA,
                 style = MaterialTheme.typography.displaySmall,
                 textAlign = TextAlign.Center,
-                color = colors.primary,
+                color = colors.onPrimary,
                 modifier =
                     Modifier.focusable().semantics(mergeDescendants = true) {
                         contentDescription = titleForEmptyStateCTA
@@ -1058,17 +1090,27 @@
                 .onSizeChanged { setToolbarSize(it) }
     ) {
         val addWidgetText = stringResource(R.string.hub_mode_add_widget_button_text)
-        ToolbarButton(
-            isPrimary = !removeEnabled,
-            modifier = Modifier.align(Alignment.CenterStart),
-            onClick = onOpenWidgetPicker,
-        ) {
-            Icon(Icons.Default.Add, null)
-            Text(text = addWidgetText)
+
+        if (!(Flags.hubEditModeTouchAdjustments() && removeEnabled)) {
+            ToolbarButton(
+                isPrimary = !removeEnabled,
+                modifier = Modifier.align(Alignment.CenterStart),
+                onClick = onOpenWidgetPicker,
+            ) {
+                Icon(Icons.Default.Add, null)
+                Text(text = addWidgetText)
+            }
         }
 
         AnimatedVisibility(
-            modifier = Modifier.align(Alignment.Center),
+            modifier =
+                Modifier.align(
+                    if (Flags.hubEditModeTouchAdjustments()) {
+                        Alignment.CenterStart
+                    } else {
+                        Alignment.Center
+                    }
+                ),
             visible = removeEnabled,
             enter = fadeIn(),
             exit = fadeOut(),
@@ -1091,7 +1133,11 @@
                     horizontalArrangement =
                         Arrangement.spacedBy(
                             ButtonDefaults.IconSpacing,
-                            Alignment.CenterHorizontally,
+                            if (Flags.hubEditModeTouchAdjustments()) {
+                                Alignment.Start
+                            } else {
+                                Alignment.CenterHorizontally
+                            },
                         ),
                     verticalAlignment = Alignment.CenterVertically,
                 ) {
@@ -1352,6 +1398,7 @@
     val shrinkWidgetLabel = stringResource(R.string.accessibility_action_label_shrink_widget)
     val expandWidgetLabel = stringResource(R.string.accessibility_action_label_expand_widget)
 
+    val isFocusable by viewModel.isFocusable.collectAsStateWithLifecycle(initialValue = false)
     val selectedKey by viewModel.selectedKey.collectAsStateWithLifecycle()
     val selectedIndex =
         selectedKey?.let { key -> contentListState.list.indexOfFirst { it.key == key } }
@@ -1465,7 +1512,8 @@
     ) {
         with(widgetSection) {
             Widget(
-                viewModel = viewModel,
+                isFocusable = isFocusable,
+                openWidgetEditor = { viewModel.onOpenWidgetEditor() },
                 model = model,
                 size = size,
                 modifier = Modifier.fillMaxSize().allowGestures(allowed = !viewModel.isEditMode),
@@ -1856,6 +1904,44 @@
         size.span
     }
 
+private fun IntRect.percentOverlap(other: IntRect): Float {
+    val intersection = intersect(other)
+    if (intersection.width < 0 || intersection.height < 0) {
+        return 0f
+    }
+    val overlapArea = intersection.width * intersection.height
+    val area = width * height
+    return overlapArea.toFloat() / area.toFloat()
+}
+
+private fun calculatePercentVisible(state: LazyGridState, index: Int): Float {
+    val viewportSize = state.layoutInfo.viewportSize
+    val visibleRect =
+        IntRect(
+            offset =
+                IntOffset(
+                    state.layoutInfo.viewportStartOffset + state.layoutInfo.beforeContentPadding,
+                    0,
+                ),
+            size =
+                IntSize(
+                    width =
+                        viewportSize.width -
+                            state.layoutInfo.beforeContentPadding -
+                            state.layoutInfo.afterContentPadding,
+                    height = viewportSize.height,
+                ),
+        )
+
+    val itemInfo = state.layoutInfo.visibleItemsInfo.find { it.index == index }
+    return if (itemInfo != null) {
+        val boundingBox = IntRect(itemInfo.offset, itemInfo.size)
+        boundingBox.percentOverlap(visibleRect)
+    } else {
+        0f
+    }
+}
+
 private object Colors {
     val DisabledColorFilter by lazy { disabledColorMatrix() }
 
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt
index ef5e90b..7a50080 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt
@@ -94,7 +94,7 @@
     private val scope: CoroutineScope,
     private val updateDragPositionForRemove: (draggingBoundingBox: IntRect) -> Boolean,
 ) {
-    var draggingItemKey by mutableStateOf<Any?>(null)
+    var draggingItemKey by mutableStateOf<String?>(null)
         private set
 
     var isDraggingToRemove by mutableStateOf(false)
@@ -138,7 +138,7 @@
             // before content padding from the initial pointer position
             .firstItemAtOffset(normalizedOffset - contentOffset)
             ?.apply {
-                draggingItemKey = key
+                draggingItemKey = key as String
                 draggingItemInitialOffset = this.offset.toOffset()
                 return true
             }
@@ -284,7 +284,9 @@
                             contentOffset,
                         )
                     ) {
-                        viewModel.onReorderWidgetStart()
+                        // draggingItemKey is guaranteed to be non-null here because it is set in
+                        // onDragStart()
+                        viewModel.onReorderWidgetStart(dragDropState.draggingItemKey!!)
                     }
                 },
                 onDragEnd = {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ResponsiveLazyHorizontalGrid.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ResponsiveLazyHorizontalGrid.kt
index 21b3474..c793054 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ResponsiveLazyHorizontalGrid.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ResponsiveLazyHorizontalGrid.kt
@@ -36,7 +36,9 @@
 import androidx.compose.runtime.remember
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.graphics.toComposeRect
 import androidx.compose.ui.platform.LocalConfiguration
+import androidx.compose.ui.platform.LocalContext
 import androidx.compose.ui.platform.LocalDensity
 import androidx.compose.ui.platform.LocalLayoutDirection
 import androidx.compose.ui.unit.Dp
@@ -45,6 +47,7 @@
 import androidx.compose.ui.unit.coerceAtMost
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.times
+import androidx.window.layout.WindowMetricsCalculator
 
 /**
  * Renders a responsive [LazyHorizontalGrid] with dynamic columns and rows. Each cell will maintain
@@ -71,7 +74,7 @@
             "$minHorizontalArrangement and $minVerticalArrangement, respectively."
     }
     BoxWithConstraints(modifier) {
-        val gridSize = rememberGridSize(maxWidth = maxWidth, maxHeight = maxHeight)
+        val gridSize = rememberGridSize()
         val layoutDirection = LocalLayoutDirection.current
         val density = LocalDensity.current
 
@@ -128,25 +131,43 @@
         val extraWidth = maxWidth - usedWidth
         val extraHeight = maxHeight - usedHeight
 
-        val finalStartPadding = minStartPadding + extraWidth / 2
+        // If there is a single column or single row, distribute extra space evenly across the grid.
+        // Otherwise, distribute it along the content padding to center the content.
+        val distributeHorizontalSpaceAlongGutters = gridSize.height == 1 || gridSize.width == 1
+        val evenlyDistributedWidth =
+            if (distributeHorizontalSpaceAlongGutters) {
+                extraWidth / (gridSize.width + 1)
+            } else {
+                extraWidth / 2
+            }
+
+        val finalStartPadding = minStartPadding + evenlyDistributedWidth
+        val finalEndPadding = minEndPadding + evenlyDistributedWidth
         val finalTopPadding = minTopPadding + extraHeight / 2
 
         val finalContentPadding =
             PaddingValues(
                 start = finalStartPadding,
-                end = minEndPadding + extraWidth / 2,
+                end = finalEndPadding,
                 top = finalTopPadding,
                 bottom = minBottomPadding + extraHeight / 2,
             )
 
         with(density) { setContentOffset(Offset(finalStartPadding.toPx(), finalTopPadding.toPx())) }
 
+        val horizontalArrangement =
+            if (distributeHorizontalSpaceAlongGutters) {
+                minHorizontalArrangement + evenlyDistributedWidth
+            } else {
+                minHorizontalArrangement
+            }
+
         LazyHorizontalGrid(
             rows = GridCells.Fixed(gridSize.height),
             modifier = Modifier.fillMaxSize(),
             state = state,
             contentPadding = finalContentPadding,
-            horizontalArrangement = Arrangement.spacedBy(minHorizontalArrangement),
+            horizontalArrangement = Arrangement.spacedBy(horizontalArrangement),
             verticalArrangement = Arrangement.spacedBy(minVerticalArrangement),
             flingBehavior = flingBehavior,
             userScrollEnabled = userScrollEnabled,
@@ -210,27 +231,38 @@
 }
 
 @Composable
-private fun rememberGridSize(maxWidth: Dp, maxHeight: Dp): IntSize {
+private fun rememberGridSize(): IntSize {
     val configuration = LocalConfiguration.current
     val orientation = configuration.orientation
+    val screenSize = calculateWindowSize()
 
-    return remember(orientation, maxWidth, maxHeight) {
+    return remember(orientation, screenSize) {
         if (orientation == Configuration.ORIENTATION_PORTRAIT) {
             IntSize(
-                width = calculateNumCellsWidth(maxWidth),
-                height = calculateNumCellsHeight(maxHeight),
+                width = calculateNumCellsWidth(screenSize.width),
+                height = calculateNumCellsHeight(screenSize.height),
             )
         } else {
             // In landscape we invert the rows/columns to ensure we match the same area as portrait.
             // This keeps the number of elements in the grid consistent when changing orientation.
             IntSize(
-                width = calculateNumCellsHeight(maxWidth),
-                height = calculateNumCellsWidth(maxHeight),
+                width = calculateNumCellsHeight(screenSize.width),
+                height = calculateNumCellsWidth(screenSize.height),
             )
         }
     }
 }
 
+@Composable
+fun calculateWindowSize(): DpSize {
+    // Observe view configuration changes and recalculate the size class on each change.
+    LocalConfiguration.current
+    val density = LocalDensity.current
+    val context = LocalContext.current
+    val metrics = WindowMetricsCalculator.getOrCreate().computeCurrentWindowMetrics(context)
+    return with(density) { metrics.bounds.toComposeRect().size.toDpSize() }
+}
+
 private fun calculateNumCellsWidth(width: Dp) =
     // See https://developer.android.com/develop/ui/views/layout/use-window-size-classes
     when {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt
index 105e8da..7956d02 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt
@@ -159,6 +159,7 @@
                     with(lockSection) { LockIcon() }
 
                     // Aligned to bottom and constrained to below the lock icon.
+                    // TODO("b/383588832") change this away from "keyguard_bottom_area"
                     Column(modifier = Modifier.fillMaxWidth().sysuiResTag("keyguard_bottom_area")) {
                         if (isUdfpsVisible && ambientIndicationSectionOptional.isPresent) {
                             with(ambientIndicationSectionOptional.get()) {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/BottomAreaSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/BottomAreaSection.kt
index 364adca..5e9ade1 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/BottomAreaSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/BottomAreaSection.kt
@@ -82,20 +82,15 @@
                             Modifier.shortcutPadding()
                         } else {
                             Modifier
-                        }
+                        },
                 )
             }
         }
     }
 
     @Composable
-    fun SceneScope.IndicationArea(
-        modifier: Modifier = Modifier,
-    ) {
-        Element(
-            key = IndicationAreaElementKey,
-            modifier = modifier.indicationAreaPadding(),
-        ) {
+    fun SceneScope.IndicationArea(modifier: Modifier = Modifier) {
+        Element(key = IndicationAreaElementKey, modifier = modifier.indicationAreaPadding()) {
             content {
                 IndicationArea(
                     indicationAreaViewModel = indicationAreaViewModel,
@@ -138,24 +133,20 @@
                             ResourcesCompat.getDrawable(
                                 context.resources,
                                 R.drawable.keyguard_bottom_affordance_bg,
-                                context.theme
+                                context.theme,
                             )
                         foreground =
                             ResourcesCompat.getDrawable(
                                 context.resources,
                                 R.drawable.keyguard_bottom_affordance_selected_border,
-                                context.theme
+                                context.theme,
                             )
                         visibility = View.INVISIBLE
                         setPadding(padding, padding, padding, padding)
                     }
 
                 setBinding(
-                    binder.bind(
-                        view,
-                        viewModel,
-                        transitionAlpha,
-                    ) {
+                    binder.bind(view, viewModel, transitionAlpha) {
                         indicationController.showTransientIndication(it)
                     }
                 )
@@ -164,10 +155,7 @@
             },
             onRelease = { binding?.destroy() },
             modifier =
-                modifier.size(
-                    width = shortcutSizeDp().width,
-                    height = shortcutSizeDp().height,
-                )
+                modifier.size(width = shortcutSizeDp().width, height = shortcutSizeDp().height),
         )
     }
 
@@ -182,6 +170,8 @@
         AndroidView(
             factory = { context ->
                 val view = KeyguardIndicationArea(context, null)
+                view.isFocusable = true
+                view.importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_YES
                 setDisposable(
                     KeyguardIndicationAreaBinder.bind(
                         view = view,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt
index e78862e..5c7ca97 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt
@@ -29,11 +29,8 @@
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.rememberCoroutineScope
-import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.platform.LocalContext
 import androidx.compose.ui.res.dimensionResource
-import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.viewinterop.AndroidView
 import androidx.lifecycle.compose.collectAsStateWithLifecycle
@@ -52,7 +49,6 @@
 import com.android.systemui.notifications.ui.composable.ConstrainedNotificationStack
 import com.android.systemui.notifications.ui.composable.SnoozeableHeadsUpNotificationSpace
 import com.android.systemui.res.R
-import com.android.systemui.shade.LargeScreenHeaderHelper
 import com.android.systemui.shade.ShadeDisplayAware
 import com.android.systemui.statusbar.notification.icon.ui.viewbinder.AlwaysOnDisplayNotificationIconViewStore
 import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerViewBinder
@@ -179,16 +175,13 @@
             return
         }
 
-        val splitShadeTopMargin: Dp =
-            LargeScreenHeaderHelper.getLargeScreenHeaderHeight(LocalContext.current).dp
-
         ConstrainedNotificationStack(
             stackScrollView = stackScrollView.get(),
             viewModel = rememberViewModel("Notifications") { viewModelFactory.create() },
             modifier =
                 modifier
                     .fillMaxWidth()
-                    .thenIf(isShadeLayoutWide) { Modifier.padding(top = splitShadeTopMargin) }
+                    .thenIf(isShadeLayoutWide) { Modifier.padding(top = 12.dp) }
                     .let {
                         if (burnInParams == null) {
                             it
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 48067ce..ef8911d 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
@@ -29,7 +29,7 @@
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.graphicsLayer
 import androidx.lifecycle.compose.collectAsStateWithLifecycle
-import com.android.compose.animation.scene.SceneScope
+import com.android.compose.animation.scene.ContentScope
 import com.android.compose.animation.scene.content.state.TransitionState
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.shade.shared.model.ShadeMode
@@ -42,7 +42,7 @@
  * transition.
  */
 @Composable
-fun SceneScope.NotificationLockscreenScrim(
+fun ContentScope.NotificationLockscreenScrim(
     viewModel: NotificationLockscreenScrimViewModel,
     modifier: Modifier = Modifier,
 ) {
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 94c18cd..cb87f0e 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
@@ -62,7 +62,6 @@
         canStartPostScroll = { offsetAvailable, _, _ ->
             offsetAvailable > 0 && (scrimOffset() < maxScrimOffset || isCurrentGestureOverscroll())
         },
-        canStartPostFling = { false },
         onStart = { firstScroll ->
             onStart(firstScroll)
             object : ScrollController {
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 d8abfd7..e1ee59b 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
@@ -25,11 +25,13 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.remember
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
 import androidx.compose.ui.input.nestedscroll.NestedScrollSource
 import androidx.compose.ui.input.nestedscroll.nestedScroll
 import androidx.compose.ui.platform.LocalConfiguration
 import androidx.compose.ui.platform.LocalDensity
 import androidx.compose.ui.unit.IntOffset
+import androidx.compose.ui.unit.Velocity
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.util.fastCoerceAtLeast
 import com.android.compose.nestedscroll.OnStopScope
@@ -80,9 +82,29 @@
         }
 
     return this.then(
-        Modifier.nestedScroll(stackNestedScrollConnection).offset {
-            IntOffset(x = 0, y = overscrollOffset.value.roundToInt())
-        }
+        Modifier.nestedScroll(
+                remember {
+                    object : NestedScrollConnection {
+                        override suspend fun onPostFling(
+                            consumed: Velocity,
+                            available: Velocity,
+                        ): Velocity {
+                            return if (available.y < 0f && !canScrollForward()) {
+                                overscrollOffset.animateTo(
+                                    targetValue = 0f,
+                                    initialVelocity = available.y,
+                                    animationSpec = tween(),
+                                )
+                                available
+                            } else {
+                                Velocity.Zero
+                            }
+                        }
+                    }
+                }
+            )
+            .nestedScroll(stackNestedScrollConnection)
+            .offset { IntOffset(x = 0, y = overscrollOffset.value.roundToInt()) }
     )
 }
 
@@ -100,7 +122,6 @@
         canStartPostScroll = { offsetAvailable, offsetBeforeStart, _ ->
             offsetAvailable < 0f && offsetBeforeStart < 0f && !canScrollForward()
         },
-        canStartPostFling = { velocityAvailable -> velocityAvailable < 0f && !canScrollForward() },
         onStart = { firstScroll ->
             onStart(firstScroll)
             object : ScrollController {
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 ae273d8..b54de78 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
@@ -45,6 +45,7 @@
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.systemBars
 import androidx.compose.foundation.layout.windowInsetsBottomHeight
+import androidx.compose.foundation.overscroll
 import androidx.compose.foundation.shape.RoundedCornerShape
 import androidx.compose.foundation.verticalScroll
 import androidx.compose.material3.MaterialTheme
@@ -84,10 +85,10 @@
 import androidx.compose.ui.util.lerp
 import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.animation.scene.ContentKey
+import com.android.compose.animation.scene.ContentScope
 import com.android.compose.animation.scene.ElementKey
 import com.android.compose.animation.scene.LowestZIndexContentPicker
 import com.android.compose.animation.scene.NestedScrollBehavior
-import com.android.compose.animation.scene.SceneScope
 import com.android.compose.animation.scene.SceneTransitionLayoutState
 import com.android.compose.animation.scene.content.state.TransitionState
 import com.android.compose.modifiers.thenIf
@@ -134,7 +135,7 @@
  * entire size of the scene.
  */
 @Composable
-fun SceneScope.HeadsUpNotificationSpace(
+fun ContentScope.HeadsUpNotificationSpace(
     stackScrollView: NotificationScrollView,
     viewModel: NotificationsPlaceholderViewModel,
     useHunBounds: () -> Boolean = { true },
@@ -176,7 +177,7 @@
  * the user. When swiped up, the heads up notification is snoozed.
  */
 @Composable
-fun SceneScope.SnoozeableHeadsUpNotificationSpace(
+fun ContentScope.SnoozeableHeadsUpNotificationSpace(
     stackScrollView: NotificationScrollView,
     viewModel: NotificationsPlaceholderViewModel,
 ) {
@@ -246,7 +247,7 @@
 
 /** Adds the space where notification stack should appear in the scene. */
 @Composable
-fun SceneScope.ConstrainedNotificationStack(
+fun ContentScope.ConstrainedNotificationStack(
     stackScrollView: NotificationScrollView,
     viewModel: NotificationsPlaceholderViewModel,
     modifier: Modifier = Modifier,
@@ -281,7 +282,7 @@
  */
 @OptIn(ExperimentalLayoutApi::class)
 @Composable
-fun SceneScope.NotificationScrollingStack(
+fun ContentScope.NotificationScrollingStack(
     shadeSession: SaveableSession,
     stackScrollView: NotificationScrollView,
     viewModel: NotificationsPlaceholderViewModel,
@@ -480,6 +481,7 @@
         modifier =
             modifier
                 .element(Notifications.Elements.NotificationScrim)
+                .overscroll(verticalOverscrollEffect)
                 .offset {
                     // if scrim is expanded while transitioning to Gone or QS scene, increase the
                     // offset in step with the corresponding transition so that it is 0 when it
@@ -622,7 +624,7 @@
  * the notification contents (stack, footer, shelf) should be drawn.
  */
 @Composable
-fun SceneScope.NotificationStackCutoffGuideline(
+fun ContentScope.NotificationStackCutoffGuideline(
     stackScrollView: NotificationScrollView,
     viewModel: NotificationsPlaceholderViewModel,
     modifier: Modifier = Modifier,
@@ -642,7 +644,7 @@
 }
 
 @Composable
-private fun SceneScope.NotificationPlaceholder(
+private fun ContentScope.NotificationPlaceholder(
     stackScrollView: NotificationScrollView,
     viewModel: NotificationsPlaceholderViewModel,
     useStackBounds: () -> Boolean,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt
index 5fb9416..e4f4df3 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt
@@ -37,6 +37,7 @@
 import com.android.systemui.qs.ui.composable.QuickSettings
 import com.android.systemui.qs.ui.composable.QuickSettings.SharedValues.MediaLandscapeTopOffset
 import com.android.systemui.qs.ui.composable.QuickSettings.SharedValues.MediaOffset.Default
+import com.android.systemui.scene.shared.model.Overlays
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.scene.ui.viewmodel.GoneUserActionsViewModel
 import com.android.systemui.statusbar.notification.stack.ui.view.NotificationScrollView
@@ -70,18 +71,22 @@
     @Composable
     override fun SceneScope.Content(modifier: Modifier) {
 
-        val isIdle by remember {
-            derivedStateOf { layoutState.transitionState is TransitionState.Idle }
+        val isIdleAndNotOccluded by remember {
+            derivedStateOf {
+                layoutState.transitionState is TransitionState.Idle &&
+                    Overlays.NotificationsShade !in layoutState.transitionState.currentOverlays
+            }
         }
 
-        LaunchedEffect(isIdle) {
+        LaunchedEffect(isIdleAndNotOccluded) {
             // Wait for being Idle on this Scene, otherwise LaunchedEffect would fire too soon,
             // and another transition could override the NSSL stack bounds.
-            if (isIdle) {
+            if (isIdleAndNotOccluded) {
                 // Reset the stack bounds to avoid caching these values from the previous Scenes,
                 // and not to confuse the StackScrollAlgorithm when it displays a HUN over GONE.
                 notificationStackScrolLView.get().apply {
-                    setStackTop(0f)
+                    // use -headsUpInset to allow HUN translation outside bounds for snoozing
+                    setStackTop(-getHeadsUpInset().toFloat())
                     setStackCutoff(0f)
                 }
             }
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 9de7a5d..55fafd5 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
@@ -33,7 +33,6 @@
 import com.android.systemui.scene.ui.composable.transitions.shadeToQuickSettingsTransition
 import com.android.systemui.scene.ui.composable.transitions.toNotificationsShadeTransition
 import com.android.systemui.scene.ui.composable.transitions.toQuickSettingsShadeTransition
-import com.android.systemui.shade.ui.composable.OverlayShade
 import com.android.systemui.shade.ui.composable.Shade
 
 /**
@@ -134,27 +133,11 @@
     }
 
     // Scene overscroll
-
+    // TODO(b/382477212) Remove STL Overscroll DSL
     overscrollDisabled(Scenes.Gone, Orientation.Vertical)
     overscrollDisabled(Scenes.Lockscreen, Orientation.Vertical)
-    overscroll(Scenes.Bouncer, Orientation.Vertical) {
-        translate(Bouncer.Elements.Content, y = { absoluteDistance })
-    }
-    overscroll(Scenes.Shade, Orientation.Vertical) {
-        translate(
-            Notifications.Elements.NotificationScrim,
-            y = Shade.Dimensions.ScrimOverscrollLimit,
-        )
-        translate(Shade.Elements.SplitShadeStartColumn, y = Shade.Dimensions.ScrimOverscrollLimit)
-        translate(
-            Notifications.Elements.NotificationStackPlaceholder,
-            y = Shade.Dimensions.ScrimOverscrollLimit,
-        )
-    }
-    overscroll(Overlays.NotificationsShade, Orientation.Vertical) {
-        translate(OverlayShade.Elements.Panel, y = OverlayShade.Dimensions.OverscrollLimit)
-    }
-    overscroll(Overlays.QuickSettingsShade, Orientation.Vertical) {
-        translate(OverlayShade.Elements.Panel, y = OverlayShade.Dimensions.OverscrollLimit)
-    }
+    overscrollDisabled(Scenes.Bouncer, Orientation.Vertical)
+    overscrollDisabled(Scenes.Shade, Orientation.Vertical)
+    overscrollDisabled(Overlays.NotificationsShade, Orientation.Vertical)
+    overscrollDisabled(Overlays.QuickSettingsShade, Orientation.Vertical)
 }
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 46f5ecd..8a5c96d 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
@@ -35,6 +35,7 @@
 import androidx.compose.foundation.layout.systemBarsIgnoringVisibility
 import androidx.compose.foundation.layout.waterfall
 import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.overscroll
 import androidx.compose.foundation.shape.RoundedCornerShape
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass
@@ -65,7 +66,10 @@
 
         Box(modifier = Modifier.fillMaxSize().panelPadding(), contentAlignment = Alignment.TopEnd) {
             Panel(
-                modifier = Modifier.element(OverlayShade.Elements.Panel).panelSize(),
+                modifier =
+                    Modifier.element(OverlayShade.Elements.Panel)
+                        .overscroll(verticalOverscrollEffect)
+                        .panelSize(),
                 content = content,
             )
         }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
index 22b6dbc..79fd1d7 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
@@ -39,6 +39,7 @@
 import androidx.compose.foundation.layout.navigationBarsPadding
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.systemBars
+import androidx.compose.foundation.overscroll
 import androidx.compose.foundation.rememberScrollState
 import androidx.compose.foundation.verticalScroll
 import androidx.compose.runtime.Composable
@@ -527,6 +528,7 @@
                 Box(
                     modifier =
                         Modifier.element(Shade.Elements.SplitShadeStartColumn)
+                            .overscroll(verticalOverscrollEffect)
                             .weight(1f)
                             .graphicsLayer { translationX = unfoldTranslationXForStartSide }
                 ) {
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 2d589f3..9744424 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
@@ -23,7 +23,7 @@
 import androidx.compose.ui.unit.round
 import androidx.compose.ui.util.fastCoerceIn
 import com.android.compose.animation.scene.content.Content
-import com.android.compose.animation.scene.content.state.TransitionState.HasOverscrollProperties.Companion.DistanceUnspecified
+import com.android.compose.animation.scene.content.state.TransitionState.DirectionProperties.Companion.DistanceUnspecified
 import com.android.compose.nestedscroll.OnStopScope
 import com.android.compose.nestedscroll.PriorityNestedScrollConnection
 import com.android.compose.nestedscroll.ScrollController
@@ -108,7 +108,7 @@
 
         swipes.updateSwipesResults(fromContent)
         val result =
-            swipes.findUserActionResult(overSlop)
+            (if (overSlop < 0f) swipes.upOrLeftResult else swipes.downOrRightResult)
                 // As we were unable to locate a valid target scene, the initial SwipeAnimation
                 // cannot be defined. Consequently, a simple NoOp Controller will be returned.
                 ?: return NoOpDragController
@@ -448,27 +448,6 @@
         this.upOrLeftResult = upOrLeftResult
         this.downOrRightResult = downOrRightResult
     }
-
-    /**
-     * Returns the [UserActionResult] in the direction of [directionOffset].
-     *
-     * @param directionOffset signed float that indicates the direction. Positive is down or right
-     *   negative is up or left.
-     * @return null when there are no targets in either direction. If one direction is null and you
-     *   drag into the null direction this function will return the opposite direction, assuming
-     *   that the users intention is to start the drag into the other direction eventually. If
-     *   [directionOffset] is 0f and both direction are available, it will default to
-     *   [upOrLeftResult].
-     */
-    fun findUserActionResult(directionOffset: Float): UserActionResult? {
-        return when {
-            upOrLeftResult == null && downOrRightResult == null -> null
-            (directionOffset < 0f && upOrLeftResult != null) || downOrRightResult == null ->
-                upOrLeftResult
-
-            else -> downOrRightResult
-        }
-    }
 }
 
 internal class NestedScrollHandlerImpl(
@@ -536,31 +515,6 @@
                     }
                 }
             },
-            canStartPostFling = { velocityAvailable ->
-                val behavior: NestedScrollBehavior =
-                    when {
-                        velocityAvailable > 0f -> topOrLeftBehavior
-                        velocityAvailable < 0f -> bottomOrRightBehavior
-                        else -> return@PriorityNestedScrollConnection false
-                    }
-
-                // We could start an overscroll animation
-                canChangeScene = false
-
-                val pointersDown: PointersInfo.PointersDown? =
-                    when (val info = pointersInfoOwner.pointersInfo()) {
-                        PointersInfo.MouseWheel -> {
-                            // Do not support mouse wheel interactions
-                            return@PriorityNestedScrollConnection false
-                        }
-
-                        is PointersInfo.PointersDown -> info
-                        null -> null
-                    }
-                lastPointersDown = pointersDown
-
-                behavior.canStartOnPostFling && shouldEnableSwipes()
-            },
             onStart = { firstScroll ->
                 scrollController(
                     dragController =
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 e819bfd..07a19d8 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
@@ -1257,7 +1257,7 @@
     }
 
     val currentContent = currentContentState.content
-    if (transition is TransitionState.HasOverscrollProperties) {
+    if (transition is TransitionState.DirectionProperties) {
         val overscroll = transition.currentOverscrollSpec
         if (overscroll?.content == currentContent) {
             val elementSpec =
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/NestedScrollToScene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/NestedScrollToScene.kt
index 955be60..9622fc1 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/NestedScrollToScene.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/NestedScrollToScene.kt
@@ -32,7 +32,7 @@
  * not consumed by the [SceneTransitionLayout] unless specifically requested via
  * [nestedScrollToScene].
  */
-enum class NestedScrollBehavior(val canStartOnPostFling: Boolean) {
+enum class NestedScrollBehavior {
     /**
      * Overscroll will only be used by the [SceneTransitionLayout] to move to the next scene if the
      * gesture begins at the edge of the scrollable component (so that a scroll in that direction
@@ -42,7 +42,7 @@
      * In addition, during scene transitions, scroll events are consumed by the
      * [SceneTransitionLayout] instead of the scrollable component.
      */
-    EdgeNoPreview(canStartOnPostFling = false),
+    EdgeNoPreview,
 
     /**
      * Overscroll will only be used by the [SceneTransitionLayout] to move to the next scene if the
@@ -52,7 +52,7 @@
      * In addition, during scene transitions, scroll events are consumed by the
      * [SceneTransitionLayout] instead of the scrollable component.
      */
-    EdgeWithPreview(canStartOnPostFling = true),
+    @Deprecated("This will be removed, see b/378470603") EdgeWithPreview,
 
     /**
      * Any overscroll will be used by the [SceneTransitionLayout] to move to the next scene.
@@ -60,7 +60,7 @@
      * In addition, during scene transitions, scroll events are consumed by the
      * [SceneTransitionLayout] instead of the scrollable component.
      */
-    EdgeAlways(canStartOnPostFling = true);
+    EdgeAlways;
 
     companion object {
         val Default = EdgeNoPreview
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
index 86c5fd8..e8b2b09 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
@@ -382,7 +382,7 @@
         // Compute the [TransformationSpec] when the transition starts.
         val fromContent = transition.fromContent
         val toContent = transition.toContent
-        val orientation = (transition as? TransitionState.HasOverscrollProperties)?.orientation
+        val orientation = (transition as? TransitionState.DirectionProperties)?.orientation
 
         // Update the transition specs.
         transition.transformationSpec =
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt
index 59d0b55..5aaeda8 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt
@@ -25,7 +25,7 @@
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.setValue
 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.animation.scene.content.state.TransitionState.DirectionProperties.Companion.DistanceUnspecified
 import kotlin.math.absoluteValue
 import kotlinx.coroutines.CompletableDeferred
 import kotlinx.coroutines.launch
@@ -197,7 +197,7 @@
     private val distance: (SwipeAnimation<T>) -> Float,
     currentContent: T = fromContent,
     dragOffset: Float = 0f,
-) : TransitionState.HasOverscrollProperties {
+) : TransitionState.DirectionProperties {
     /** The [TransitionState.Transition] whose implementation delegates to this [SwipeAnimation]. */
     lateinit var contentTransition: TransitionState.Transition
 
@@ -513,7 +513,7 @@
         swipeAnimation.toContent,
         replacedTransition,
     ),
-    TransitionState.HasOverscrollProperties by swipeAnimation {
+    TransitionState.DirectionProperties by swipeAnimation {
 
     constructor(
         other: ChangeSceneSwipeTransition
@@ -575,7 +575,7 @@
         swipeAnimation.toContent,
         replacedTransition,
     ),
-    TransitionState.HasOverscrollProperties by swipeAnimation {
+    TransitionState.DirectionProperties by swipeAnimation {
     constructor(
         other: ShowOrHideOverlaySwipeTransition
     ) : this(
@@ -634,7 +634,7 @@
         swipeAnimation.toContent,
         replacedTransition,
     ),
-    TransitionState.HasOverscrollProperties by swipeAnimation {
+    TransitionState.DirectionProperties by swipeAnimation {
     constructor(
         other: ReplaceOverlaySwipeTransition
     ) : this(
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt
index 48f08a7..952668a 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt
@@ -111,6 +111,9 @@
      * The overscroll animation always starts from a progress of 0f, and reaches 1f when moving the
      * [distance] down/right, -1f when moving in the opposite direction.
      */
+    @Deprecated(
+        "Use verticalOverscrollEffect (or horizontalOverscrollEffect) directly from SceneScope."
+    )
     fun overscroll(
         content: ContentKey,
         orientation: Orientation,
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/state/TransitionState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/state/TransitionState.kt
index d66fe42..29be445 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/state/TransitionState.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/state/TransitionState.kt
@@ -273,7 +273,7 @@
          * every time progress is changed.
          */
         private val _currentOverscrollSpec: State<OverscrollSpecImpl?>? =
-            if (this !is HasOverscrollProperties) {
+            if (this !is DirectionProperties) {
                 null
             } else {
                 derivedStateOf {
@@ -406,7 +406,7 @@
         /** Returns if the [progress] value of this transition can go beyond range `[0; 1]` */
         internal fun isWithinProgressRange(progress: Float): Boolean {
             // If the properties are missing we assume that every [Transition] can overscroll
-            if (this !is HasOverscrollProperties) return true
+            if (this !is DirectionProperties) return true
             // [OverscrollSpec] for the current scene, even if it hasn't started overscrolling yet.
             val specForCurrentScene =
                 when {
@@ -444,7 +444,7 @@
         }
     }
 
-    interface HasOverscrollProperties {
+    interface DirectionProperties {
         /**
          * The position of the [Transition.toContent].
          *
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/reveal/ContainerReveal.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/reveal/ContainerReveal.kt
index bfb5ca7..944bd85 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/reveal/ContainerReveal.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/reveal/ContainerReveal.kt
@@ -157,7 +157,7 @@
         val idleSize = checkNotNull(element.targetSize(content))
         val userActionDistance = idleSize.height
         val progress =
-            when ((transition as? TransitionState.HasOverscrollProperties)?.bouncingContent) {
+            when ((transition as? TransitionState.DirectionProperties)?.bouncingContent) {
                 null -> transition.progressTo(content)
                 content -> 1f
                 else -> 0f
@@ -256,7 +256,7 @@
 
     private fun targetAlpha(transition: TransitionState.Transition, content: ContentKey): Float {
         if (transition.isUserInputOngoing) {
-            if (transition !is TransitionState.HasOverscrollProperties) {
+            if (transition !is TransitionState.DirectionProperties) {
                 error(
                     "Unsupported transition driven by user input but that does not have " +
                         "overscroll properties: $transition"
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 2f4d5bff..432add3 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
@@ -60,7 +60,7 @@
         // As this object is created by OverscrollBuilderImpl and we retrieve the current
         // OverscrollSpec only when the transition implements HasOverscrollProperties, we can assume
         // that this method was invoked after performing this check.
-        val overscrollProperties = transition as TransitionState.HasOverscrollProperties
+        val overscrollProperties = transition as TransitionState.DirectionProperties
         val overscrollScope =
             cachedOverscrollScope.getFromCacheOrCompute(density = this, overscrollProperties)
 
@@ -77,17 +77,17 @@
 
 /**
  * A helper class to cache a [OverscrollScope] given a [Density] and
- * [TransitionState.HasOverscrollProperties]. This helps avoid recreating a scope every frame
- * whenever an overscroll transition is computed.
+ * [TransitionState.DirectionProperties]. This helps avoid recreating a scope every frame whenever
+ * an overscroll transition is computed.
  */
 private class CachedOverscrollScope {
     private var previousScope: OverscrollScope? = null
     private var previousDensity: Density? = null
-    private var previousOverscrollProperties: TransitionState.HasOverscrollProperties? = null
+    private var previousOverscrollProperties: TransitionState.DirectionProperties? = null
 
     fun getFromCacheOrCompute(
         density: Density,
-        overscrollProperties: TransitionState.HasOverscrollProperties,
+        overscrollProperties: TransitionState.DirectionProperties,
     ): OverscrollScope {
         if (
             previousScope == null ||
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 98a0017..b26bf55 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
@@ -56,7 +56,6 @@
         canStartPostScroll = { offsetAvailable, _, _ ->
             offsetAvailable > 0 && height() < maxHeight()
         },
-        canStartPostFling = { false },
         onStart = {
             LargeTopAppBarScrollController(
                 height = height,
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 3f18236..3d0f182 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
@@ -24,7 +24,6 @@
 import androidx.compose.ui.input.nestedscroll.NestedScrollSource
 import androidx.compose.ui.unit.Velocity
 import com.android.compose.ui.util.SpaceVectorConverter
-import kotlin.math.sign
 import kotlinx.coroutines.Deferred
 import kotlinx.coroutines.async
 import kotlinx.coroutines.coroutineScope
@@ -102,8 +101,8 @@
  * over the default nested scrolling logic.
  *
  * When started, this connection intercepts scroll events *before* they reach child composables.
- * This "priority mode" is activated activated when either [canStartPreScroll], [canStartPostScroll]
- * or [canStartPostFling] returns `true`.
+ * This "priority mode" is activated when either [canStartPreScroll] or [canStartPostScroll] returns
+ * `true`.
  *
  * Once started, the [onStart] lambda provides a [ScrollController] to manage the scrolling. This
  * controller allows you to directly manipulate the scroll state and define how scroll events are
@@ -123,8 +122,6 @@
  * @param canStartPostScroll A lambda that returns `true` if the connection should enter priority
  *   mode during the post-scroll phase. This is called after child connections have consumed the
  *   scroll.
- * @param canStartPostFling A lambda that returns `true` if the connection should enter priority
- *   mode during the post-fling phase. This is called after a fling gesture has been initiated.
  * @param onStart A lambda that is called when the connection enters priority mode. It should return
  *   a [ScrollController] that will be used to control the scroll.
  * @sample LargeTopAppBarNestedScrollConnection
@@ -136,7 +133,6 @@
         (offsetAvailable: Float, offsetBeforeStart: Float, source: NestedScrollSource) -> Boolean,
     private val canStartPostScroll:
         (offsetAvailable: Float, offsetBeforeStart: Float, source: NestedScrollSource) -> Boolean,
-    private val canStartPostFling: (velocityAvailable: Float) -> Boolean,
     private val onStart: (firstScroll: Float) -> ScrollController,
 ) : NestedScrollConnection, SpaceVectorConverter by SpaceVectorConverter(orientation) {
 
@@ -233,17 +229,6 @@
             return stop(velocity = availableFloat)
         }
 
-        // Check if post-fling condition is met, and start priority mode if necessary.
-        // TODO(b/291053278): Remove canStartPostFling() and instead make it possible to define the
-        // overscroll behavior on the Scene level.
-        if (canStartPostFling(availableFloat)) {
-            // The offset passed to onPriorityStart() must be != 0f, so we create a small offset of
-            // 1px given the available velocity.
-            val smallOffset = availableFloat.sign
-            start(availableOffset = smallOffset)
-            return stop(availableFloat)
-        }
-
         // Reset offset tracking after the fling gesture is finished.
         resetOffsetTracker()
         return Velocity.Zero
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt
index b20056d..6985644 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt
@@ -19,7 +19,9 @@
 import androidx.compose.animation.core.Spring
 import androidx.compose.animation.core.spring
 import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.foundation.overscroll
 import androidx.compose.material3.Text
+import androidx.compose.ui.Modifier
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
 import androidx.compose.ui.input.nestedscroll.NestedScrollSource.Companion.UserInput
@@ -102,7 +104,7 @@
                 userActions =
                     mapOf(Swipe.Up to SceneB, Swipe.Up(fromSource = Edge.Bottom) to SceneA),
             ) {
-                Text("SceneC")
+                Text("SceneC", Modifier.overscroll(verticalOverscrollEffect))
             }
             overlay(
                 key = OverlayA,
@@ -434,35 +436,12 @@
     }
 
     @Test
-    fun onDragIntoNoAction_startTransitionToOppositeDirection() = runGestureTest {
+    fun onDragIntoNoAction_stayIdle() = runGestureTest {
         navigateToSceneC()
 
         // We are on SceneC which has no action in Down direction
-        val dragController = onDragStarted(overSlop = 10f)
-        assertTransition(
-            currentScene = SceneC,
-            fromScene = SceneC,
-            toScene = SceneB,
-            progress = -0.1f,
-        )
-
-        // Reverse drag direction, it will consume the previous drag
-        dragController.onDragDelta(pixels = -10f)
-        assertTransition(
-            currentScene = SceneC,
-            fromScene = SceneC,
-            toScene = SceneB,
-            progress = 0.0f,
-        )
-
-        // Continue reverse drag direction, it should record progress to Scene B
-        dragController.onDragDelta(pixels = -10f)
-        assertTransition(
-            currentScene = SceneC,
-            fromScene = SceneC,
-            toScene = SceneB,
-            progress = 0.1f,
-        )
+        onDragStarted(overSlop = 10f, expectedConsumedOverSlop = 0f)
+        assertIdle(currentScene = SceneC)
     }
 
     @Test
@@ -942,30 +921,6 @@
     }
 
     @Test
-    fun scrollFromIdleWithNoTargetScene_shouldUseOverscrollSpecIfAvailable() = runGestureTest {
-        layoutState.transitions = transitions {
-            overscroll(SceneC, Orientation.Vertical) { fade(TestElements.Foo) }
-        }
-        // Start at scene C.
-        navigateToSceneC()
-
-        val scene = layoutState.transitionState.currentScene
-        // We should have overscroll spec for scene C
-        assertThat(layoutState.transitions.overscrollSpec(scene, Orientation.Vertical)).isNotNull()
-        assertThat(layoutState.currentTransition?.currentOverscrollSpec).isNull()
-
-        val nestedScroll = nestedScrollConnection(nestedScrollBehavior = EdgeAlways)
-        nestedScroll.scroll(available = downOffset(fractionOfScreen = 0.1f))
-
-        // We scrolled down, under scene C there is nothing, so we can use the overscroll spec
-        assertThat(layoutState.currentTransition?.currentOverscrollSpec).isNotNull()
-        assertThat(layoutState.currentTransition?.currentOverscrollSpec?.content).isEqualTo(SceneC)
-        val transition = layoutState.currentTransition
-        assertThat(transition).isNotNull()
-        assertThat(transition!!.progress).isEqualTo(-0.1f)
-    }
-
-    @Test
     fun nestedScrollUseFromSourceInfo() = runGestureTest {
         // Start at scene C.
         navigateToSceneC()
@@ -1229,72 +1184,6 @@
     }
 
     @Test
-    fun overscroll_releaseAtNegativePercent_up() = runGestureTest {
-        // Make Scene A overscrollable.
-        layoutState.transitions = transitions {
-            from(SceneA, to = SceneB) { spec = spring(dampingRatio = Spring.DampingRatioNoBouncy) }
-            overscroll(SceneA, Orientation.Vertical) { fade(TestElements.Foo) }
-        }
-
-        mutableUserActionsA = mapOf(Swipe.Up to UserActionResult(SceneB))
-
-        val middle = pointersDown(startedPosition = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f))
-        val dragController = onDragStarted(pointersInfo = middle, overSlop = down(1f))
-        val transition = assertThat(transitionState).isSceneTransition()
-        assertThat(transition).hasFromScene(SceneA)
-        assertThat(transition).hasToScene(SceneB)
-        assertThat(transition).hasProgress(-1f)
-
-        // Release to A.
-        dragController.onDragStoppedAnimateNow(
-            velocity = 0f,
-            onAnimationStart = {
-                assertTransition(fromScene = SceneA, toScene = SceneB, progress = -1f)
-            },
-            expectedConsumedVelocity = 0f,
-        )
-
-        // We kept the overscroll at 100% so that the placement logic didn't change at the end of
-        // the animation.
-        assertIdle(SceneA)
-        assertThat(transition).hasProgress(0f)
-        assertThat(transition).hasOverscrollSpec()
-    }
-
-    @Test
-    fun overscroll_releaseAtNegativePercent_down() = runGestureTest {
-        // Make Scene A overscrollable.
-        layoutState.transitions = transitions {
-            from(SceneA, to = SceneC) { spec = spring(dampingRatio = Spring.DampingRatioNoBouncy) }
-            overscroll(SceneA, Orientation.Vertical) { fade(TestElements.Foo) }
-        }
-
-        mutableUserActionsA = mapOf(Swipe.Down to UserActionResult(SceneC))
-
-        val middle = pointersDown(startedPosition = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f))
-        val dragController = onDragStarted(pointersInfo = middle, overSlop = up(1f))
-        val transition = assertThat(transitionState).isSceneTransition()
-        assertThat(transition).hasFromScene(SceneA)
-        assertThat(transition).hasToScene(SceneC)
-        assertThat(transition).hasProgress(-1f)
-
-        // Release to A.
-        dragController.onDragStoppedAnimateNow(
-            velocity = 0f,
-            onAnimationStart = {
-                assertTransition(fromScene = SceneA, toScene = SceneC, progress = -1f)
-            },
-            expectedConsumedVelocity = 0f,
-        )
-
-        // We kept the overscroll at 100% so that the placement logic didn't change at the end of
-        // the animation.
-        assertIdle(SceneA)
-        assertThat(transition).hasProgress(0f)
-        assertThat(transition).hasOverscrollSpec()
-    }
-
-    @Test
     fun requireFullDistanceSwipe() = runGestureTest {
         mutableUserActionsA +=
             Swipe.Up to UserActionResult(SceneB, requiresFullDistanceSwipe = true)
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
index 4410e15..ffba639 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
@@ -1006,77 +1006,74 @@
 
     @Test
     fun elementTransitionDuringNestedScrollOverscroll() {
+        lateinit var density: Density
         // The draggable touch slop, i.e. the min px distance a touch pointer must move before it is
         // detected as a drag event.
         var touchSlop = 0f
-        val overscrollTranslateY = 10.dp
         val layoutWidth = 200.dp
         val layoutHeight = 400.dp
 
         val state =
             rule.runOnUiThread {
                 MutableSceneTransitionLayoutState(
-                    initialScene = SceneB,
-                    transitions =
-                        transitions {
-                            overscroll(SceneB, Orientation.Vertical) {
-                                progressConverter = ProgressConverter.linear()
-                                translate(TestElements.Foo, y = overscrollTranslateY)
-                            }
-                        },
+                    initialScene = SceneA,
+                    transitions = transitions { overscrollDisabled(SceneB, Orientation.Vertical) },
                 )
-                    as MutableSceneTransitionLayoutStateImpl
             }
 
         rule.setContent {
+            density = LocalDensity.current
             touchSlop = LocalViewConfiguration.current.touchSlop
             SceneTransitionLayout(
                 state = state,
                 modifier = Modifier.size(layoutWidth, layoutHeight),
             ) {
-                scene(SceneA) { Spacer(Modifier.fillMaxSize()) }
-                scene(SceneB, userActions = mapOf(Swipe.Up to SceneA)) {
+                scene(SceneA, userActions = mapOf(Swipe.Down to SceneB)) {
                     Box(
                         Modifier
                             // A scrollable that does not consume the scroll gesture
                             .scrollable(
-                                rememberScrollableState(consumeScrollDelta = { 0f }),
-                                Orientation.Vertical,
+                                state = rememberScrollableState(consumeScrollDelta = { 0f }),
+                                orientation = Orientation.Vertical,
                             )
                             .fillMaxSize()
-                    ) {
-                        Spacer(Modifier.element(TestElements.Foo).fillMaxSize())
-                    }
+                    )
+                }
+                scene(SceneB) {
+                    Spacer(
+                        Modifier.overscroll(verticalOverscrollEffect)
+                            .element(TestElements.Foo)
+                            .fillMaxSize()
+                    )
                 }
             }
         }
 
         assertThat(state.transitionState).isIdle()
-        val fooElement = rule.onNodeWithTag(TestElements.Foo.testTag)
-        fooElement.assertTopPositionInRootIsEqualTo(0.dp)
+        rule.onNodeWithTag(TestElements.Foo.testTag).assertDoesNotExist()
 
         // Swipe by half of verticalSwipeDistance.
         rule.onRoot().performTouchInput {
             val middleTop = Offset((layoutWidth / 2).toPx(), 0f)
             down(middleTop)
-            // Scroll 50%
+            // Scroll 50%.
             moveBy(Offset(0f, touchSlop + layoutHeight.toPx() * 0.5f), delayMillis = 1_000)
         }
 
         val transition = assertThat(state.transitionState).isSceneTransition()
-        assertThat(transition).hasOverscrollSpec()
-        assertThat(transition).hasProgress(-0.5f)
-        fooElement.assertTopPositionInRootIsEqualTo(overscrollTranslateY * 0.5f)
+        assertThat(transition).hasProgress(0.5f)
+        rule.onNodeWithTag(TestElements.Foo.testTag).assertTopPositionInRootIsEqualTo(0.dp)
 
         rule.onRoot().performTouchInput {
-            // Scroll another 100%
+            // Scroll another 100%.
             moveBy(Offset(0f, layoutHeight.toPx()), delayMillis = 1_000)
         }
 
-        // Scroll 150% (Scene B overscroll by 50%)
-        assertThat(transition).hasProgress(-1.5f)
-        assertThat(transition).hasOverscrollSpec()
-        fooElement.assertTopPositionInRootIsEqualTo(overscrollTranslateY * 1.5f)
+        // Scroll 150% (Scene B overscroll by 50%).
+        assertThat(transition).hasProgress(1f)
+        rule
+            .onNodeWithTag(TestElements.Foo.testTag)
+            .assertTopPositionInRootIsEqualTo(expectedOffset(layoutHeight * 0.5f, density))
     }
 
     @Test
@@ -2856,7 +2853,7 @@
         // Start an overscrollable transition driven by progress.
         var progress by mutableFloatStateOf(0f)
         val transition = transition(from = SceneA, to = SceneB, progress = { progress })
-        assertThat(transition).isInstanceOf(TransitionState.HasOverscrollProperties::class.java)
+        assertThat(transition).isInstanceOf(TransitionState.DirectionProperties::class.java)
         scope.launch { state.startTransition(transition) }
 
         // Reset the counters after the first animation frame.
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/subjects/TransitionStateSubject.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/subjects/TransitionStateSubject.kt
index 0adb480..9a2af64 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/subjects/TransitionStateSubject.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/subjects/TransitionStateSubject.kt
@@ -168,12 +168,12 @@
 
     fun hasBouncingContent(content: ContentKey) {
         val actual = actual
-        if (actual !is TransitionState.HasOverscrollProperties) {
+        if (actual !is TransitionState.DirectionProperties) {
             failWithActual(simpleFact("expected to be ContentState.HasOverscrollProperties"))
         }
 
         check("bouncingContent")
-            .that((actual as TransitionState.HasOverscrollProperties).bouncingContent)
+            .that((actual as TransitionState.DirectionProperties).bouncingContent)
             .isEqualTo(content)
     }
 }
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 28ea2d2..51483a8 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
@@ -39,7 +39,6 @@
 class PriorityNestedScrollConnectionTest {
     private var canStartPreScroll = false
     private var canStartPostScroll = false
-    private var canStartPostFling = false
     private var canStopOnPreFling = true
     private var isStarted = false
     private var lastScroll: Float? = null
@@ -63,7 +62,6 @@
             orientation = Orientation.Vertical,
             canStartPreScroll = { _, _, _ -> canStartPreScroll },
             canStartPostScroll = { _, _, _ -> canStartPostScroll },
-            canStartPostFling = { canStartPostFling },
             onStart = { _ ->
                 isStarted = true
                 object : ScrollController {
@@ -239,36 +237,6 @@
     }
 
     @Test
-    fun receive_onPostFling() = runTest {
-        canStartPostFling = true
-
-        scrollConnection.onPostFling(consumed = Velocity(1f, 1f), available = Velocity(2f, 2f))
-
-        assertThat(lastStop).isEqualTo(2f)
-    }
-
-    @Test
-    fun step1_priorityModeShouldStartOnlyOnPostFling() = runTest {
-        canStartPostFling = true
-
-        scrollConnection.onPreScroll(available = Offset.Zero, source = UserInput)
-        assertThat(isStarted).isEqualTo(false)
-
-        scrollConnection.onPostScroll(
-            consumed = Offset.Zero,
-            available = Offset.Zero,
-            source = UserInput,
-        )
-        assertThat(isStarted).isEqualTo(false)
-
-        scrollConnection.onPreFling(available = Velocity.Zero)
-        assertThat(isStarted).isEqualTo(false)
-
-        scrollConnection.onPostFling(consumed = Velocity.Zero, available = Velocity.Zero)
-        assertThat(isStarted).isEqualTo(true)
-    }
-
-    @Test
     fun handleMultipleOnPreFlingCalls() = runTest {
         startPriorityModePostScroll()
 
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/TestOverlayTransition.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/TestOverlayTransition.kt
index 646cff8..6015479 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/TestOverlayTransition.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/TestOverlayTransition.kt
@@ -71,7 +71,7 @@
 ): TestOverlayTransition {
     return object :
         TestOverlayTransition(fromScene, overlay, replacedTransition),
-        TransitionState.HasOverscrollProperties {
+        TransitionState.DirectionProperties {
         override val isEffectivelyShown: Boolean
             get() = isEffectivelyShown()
 
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/TestReplaceOverlayTransition.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/TestReplaceOverlayTransition.kt
index c342f48..bd2118d 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/TestReplaceOverlayTransition.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/TestReplaceOverlayTransition.kt
@@ -68,7 +68,7 @@
 ): TestReplaceOverlayTransition {
     return object :
         TestReplaceOverlayTransition(from, to, replacedTransition),
-        TransitionState.HasOverscrollProperties {
+        TransitionState.DirectionProperties {
         override val effectivelyShownOverlay: OverlayKey
             get() = effectivelyShownOverlay()
 
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/TestSceneTransition.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/TestSceneTransition.kt
index d24b895..1d27e3a 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/TestSceneTransition.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/TestSceneTransition.kt
@@ -62,7 +62,7 @@
     replacedTransition: Transition? = null,
 ): TestSceneTransition {
     return object :
-        TestSceneTransition(from, to, replacedTransition), TransitionState.HasOverscrollProperties {
+        TestSceneTransition(from, to, replacedTransition), TransitionState.DirectionProperties {
         override val currentScene: SceneKey
             get() = current()
 
diff --git a/packages/SystemUI/lint-baseline.xml b/packages/SystemUI/lint-baseline.xml
index 523adc6..b0963d3 100644
--- a/packages/SystemUI/lint-baseline.xml
+++ b/packages/SystemUI/lint-baseline.xml
@@ -32784,1181 +32784,4 @@
             column="23"/>
     </issue>
 
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="constructor(@Main private val resources: Resources, val theme: Theme) :"
-        errorLine2="            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/domain/AirplaneModeMapper.kt"
-            line="33"
-            column="13"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="    @Main private val resources: Resources,"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapper.kt"
-            line="39"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="            @NonNull Context context,"
-        errorLine2="                             ~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java"
-            line="300"
-            column="30"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="            Context context, DeviceConfigProxy proxy) {"
-        errorLine2="                    ~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/AssistantFeedbackController.java"
-            line="75"
-            column="21"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="    public AuthController(Context context,"
-        errorLine2="                                  ~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java"
-            line="716"
-            column="35"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated WindowManager, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of WindowManager is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="            @NonNull WindowManager windowManager,"
-        errorLine2="                                   ~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java"
-            line="721"
-            column="36"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="    private val sysuiContext: Context,"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt"
-            line="72"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated ConfigurationController, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of ConfigurationController is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="    private val configurationController: ConfigurationController,"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt"
-            line="74"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="constructor(@Main protected val resources: Resources, private val theme: Resources.Theme) :"
-        errorLine2="            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/battery/ui/BatterySaverTileMapper.kt"
-            line="32"
-            column="13"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="            Context context,"
-        errorLine2="                    ~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/biometrics/BiometricNotificationBroadcastReceiver.java"
-            line="46"
-            column="21"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="            @Main Resources resources,"
-        errorLine2="                            ~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/biometrics/BiometricNotificationDialogFactory.java"
-            line="52"
-            column="29"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="    public BiometricNotificationService(@NonNull Context context,"
-        errorLine2="                                                         ~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/biometrics/BiometricNotificationService.java"
-            line="148"
-            column="58"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="    @Application private val applicationContext: Context,"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractor.kt"
-            line="62"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="    @Application private val applicationContext: Context,"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/BouncerRepository.kt"
-            line="37"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="    c: Context,"
-        errorLine2="    ~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt"
-            line="61"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="constructor(@Main private val resources: Resources, private val theme: Resources.Theme) :"
-        errorLine2="            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/ColorCorrectionTileMapper.kt"
-            line="32"
-            column="13"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="constructor(@Main private val resources: Resources, private val theme: Theme) :"
-        errorLine2="            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/inversion/domain/ColorInversionTileMapper.kt"
-            line="33"
-            column="13"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="    @Application private val applicationContext: Context,"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/CredentialInteractor.kt"
-            line="52"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="    @Application private val applicationContext: Context,"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/CredentialViewModel.kt"
-            line="30"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="class CustomTileStatePersisterImpl @Inject constructor(context: Context) :"
-        errorLine2="                                                       ~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/external/CustomTileStatePersister.kt"
-            line="74"
-            column="56"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="constructor(@Main private val resources: Resources, private val theme: Resources.Theme) :"
-        errorLine2="            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapper.kt"
-            line="32"
-            column="13"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="    @Main private val resources: Resources,"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/DefaultTilesRepository.kt"
-            line="18"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="    @Application context: Context,"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/DisplayStateInteractor.kt"
-            line="77"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="    @Application val context: Context,"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/DisplayStateRepository.kt"
-            line="68"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="    @Main private val resources: Resources,"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/EmergencyServicesRepository.kt"
-            line="38"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="    @Main private val resources: Resources,"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/biometrics/FaceAuthAccessibilityDelegate.kt"
-            line="37"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="    @Main private val resources: Resources,"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/biometrics/FaceHelpMessageDeferral.kt"
-            line="42"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="    @Application val applicationContext: Context,"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/FacePropertyRepository.kt"
-            line="95"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="    @Main private val resources: Resources,"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt"
-            line="142"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="    @Application private val context: Context,"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/FingerprintPropertyInteractor.kt"
-            line="44"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="constructor(@Main private val resources: Resources, private val theme: Theme) :"
-        errorLine2="            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapper.kt"
-            line="33"
-            column="13"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="constructor(@Main private val resources: Resources, private val theme: Resources.Theme) :"
-        errorLine2="            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/FontScalingTileMapper.kt"
-            line="32"
-            column="13"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="            @NonNull final Context context,"
-        errorLine2="                                   ~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImpl.java"
-            line="186"
-            column="36"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated ConfigurationController, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of ConfigurationController is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="            ConfigurationController configurationController,"
-        errorLine2="                                    ~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImpl.java"
-            line="192"
-            column="37"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="constructor(@Main private val resources: Resources, private val theme: Resources.Theme) :"
-        errorLine2="            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapper.kt"
-            line="32"
-            column="13"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="class IconBuilder @Inject constructor(private val context: Context) {"
-        errorLine2="                                      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconBuilder.kt"
-            line="27"
-            column="39"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="constructor(@Main private val resources: Resources, private val theme: Theme) :"
-        errorLine2="            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingMapper.kt"
-            line="31"
-            column="13"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated WindowManager, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of WindowManager is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="    private val windowManager: WindowManager,"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/keyboard/docking/ui/viewmodel/KeyboardDockingIndicationViewModel.kt"
-            line="39"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="    private val context: Context,"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/keyboard/docking/ui/viewmodel/KeyboardDockingIndicationViewModel.kt"
-            line="40"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="    @Main private val resources: Resources,"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/LargeTileSpanRepository.kt"
-            line="41"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="constructor(@Main private val resources: Resources, private val theme: Theme) :"
-        errorLine2="            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapper.kt"
-            line="33"
-            column="13"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated LayoutInflater, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of LayoutInflater is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="    private val layoutInflater: LayoutInflater"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/MediaContainerController.kt"
-            line="29"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="class MinimumTilesResourceRepository @Inject constructor(@Main resources: Resources) :"
-        errorLine2="                                                         ~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/MinimumTilesRepository.kt"
-            line="38"
-            column="58"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="constructor(@Main private val resources: Resources, val theme: Resources.Theme) :"
-        errorLine2="            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapper.kt"
-            line="33"
-            column="13"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="    @Application private val context: Context,"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/night/domain/interactor/NightDisplayTileDataInteractor.kt"
-            line="38"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="    @Main private val resources: Resources,"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/night/ui/NightDisplayTileMapper.kt"
-            line="42"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="constructor(@Main private val resources: Resources, private val theme: Resources.Theme) :"
-        errorLine2="            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/notes/domain/NotesTileMapper.kt"
-            line="32"
-            column="13"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="    public NotificationGutsManager(Context context,"
-        errorLine2="                                           ~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java"
-            line="137"
-            column="44"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="    @Main resources: Resources,"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModel.kt"
-            line="47"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="    @Main resources: Resources,"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModel.kt"
-            line="53"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="constructor(val context: Context) {"
-        errorLine2="            ~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManager.kt"
-            line="27"
-            column="13"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated ConfigurationController, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of ConfigurationController is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="            ConfigurationController configurationController,"
-        errorLine2="                                    ~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java"
-            line="737"
-            column="37"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="    @Main private val resources: Resources,"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt"
-            line="63"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="        Builder(@Main Resources resources, ViewConfiguration viewConfiguration,"
-        errorLine2="                                ~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java"
-            line="563"
-            column="33"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="constructor(@Main private val resources: Resources, private val theme: Resources.Theme) :"
-        errorLine2="            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/onehanded/ui/OneHandedModeTileMapper.kt"
-            line="32"
-            column="13"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="    public PackageManagerAdapter(Context context) {"
-        errorLine2="                                         ~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/external/PackageManagerAdapter.java"
-            line="45"
-            column="42"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="    @Main private val resources: Resources,"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/PaginatedGridRepository.kt"
-            line="36"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="    private val context: Context,"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt"
-            line="74"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="    private val context: Context,"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractor.kt"
-            line="41"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="    @Application private val context: Context,"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt"
-            line="82"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="constructor(@Main private val resources: Resources, private val theme: Resources.Theme) :"
-        errorLine2="            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/ui/QRCodeScannerTileMapper.kt"
-            line="32"
-            column="13"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="    @Main private val resources: Resources,"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/QSColumnsRepository.kt"
-            line="36"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="        Factory(Context context, QSCustomizerController qsCustomizerController) {"
-        errorLine2="                        ~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/QSTileRevealController.java"
-            line="99"
-            column="25"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="    @Main private val resources: Resources,"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/QuickQuickSettingsRowRepository.kt"
-            line="32"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="constructor(@Main private val resources: Resources, private val theme: Resources.Theme) :"
-        errorLine2="            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/reducebrightness/ui/ReduceBrightColorsTileMapper.kt"
-            line="33"
-            column="13"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="    @Main private val resources: Resources,"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileUserActionInteractor.kt"
-            line="36"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="    @Main private val resources: Resources,"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/domain/interactor/RotationLockTileDataInteractor.kt"
-            line="47"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="    @Main private val resources: Resources,"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapper.kt"
-            line="36"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="    @Main private val resources: Resources,"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/SafetyCenterAutoAddable.kt"
-            line="51"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="constructor(@Main private val resources: Resources, private val theme: Resources.Theme) :"
-        errorLine2="            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/ui/ScreenRecordTileMapper.kt"
-            line="33"
-            column="13"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated LayoutInflater, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of LayoutInflater is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="    private val layoutInflater: LayoutInflater,"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/SectionHeaderController.kt"
-            line="43"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="    context: Context"
-        errorLine2="    ~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/SectionHeaderVisibilityProvider.kt"
-            line="35"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="    @Application private val applicationContext: Context,"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinder.kt"
-            line="63"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="    @Application private val applicationContext: Context,"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModel.kt"
-            line="53"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="    private val context: Context,"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractor.kt"
-            line="51"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated WindowManager, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of WindowManager is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="    windowManager: WindowManager,"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractor.kt"
-            line="53"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="    @Application private val applicationContext: Context,"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/SimBouncerInteractor.kt"
-            line="60"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="    @Main private val resources: Resources,"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/SimBouncerInteractor.kt"
-            line="65"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only @ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme). If the usage of Resources is not related to display specific configuration or UI, then there is technically no need to use the annotation, and you can annotate the class with @SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="    @Main resources: Resources,"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/SimBouncerRepository.kt"
-            line="91"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only @ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme). If the usage of Context is not related to display specific configuration or UI, then there is technically no need to use the annotation, and you can annotate the class with @SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="constructor(context: Context, val shadeViewController: ShadeViewController) {"
-        errorLine2="            ~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/shade/StatusBarLongPressGestureDetector.kt"
-            line="30"
-            column="13"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="    @Main private val resources: Resources,"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/StockTilesRepository.kt"
-            line="31"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="    private val context: Context"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/TargetSdkResolver.kt"
-            line="33"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="    @Main private val resources: Resources,"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/TileSpecRepository.kt"
-            line="95"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only @ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme). If the usage of Resources is not related to display specific configuration or UI, then there is technically no need to use the annotation, and you can annotate the class with @SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="    @Main private val resources: Resources,"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardAccessibilityDelegate.kt"
-            line="33"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="    @Application private val context: Context,"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractor.kt"
-            line="49"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated ConfigurationController, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of ConfigurationController is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="    private val configurationController: ConfigurationController,"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/interactor/UiModeNightTileDataInteractor.kt"
-            line="43"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="constructor(@Main private val resources: Resources, private val theme: Theme) :"
-        errorLine2="            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileMapper.kt"
-            line="37"
-            column="13"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="    @Main private val resources: Resources,"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/work/ui/WorkModeTileMapper.kt"
-            line="36"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated ConfigurationInteractor, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of ConfigurationInteractor is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="    @GlobalConfig configurationInteractor: ConfigurationInteractor,"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/keyboard/docking/ui/viewmodel/KeyboardDockingIndicationViewModel.kt"
-            line="43"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only @ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme). If the usage of Context is not related to display specific configuration or UI, then there is technically no need to use the annotation, and you can annotate the class with @SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="    public AuthController(Context context,"
-        errorLine2="                                  ~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java"
-            line="716"
-            column="35"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window should use ShadeDisplayAware-annotated WindowManager, as the shade might move between windows, and only @ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme). If the usage of WindowManager is not related to display specific configuration or UI, then there is technically no need to use the annotation, and you can annotate the class with @SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="            @NonNull WindowManager windowManager,"
-        errorLine2="                                   ~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java"
-            line="721"
-            column="36"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only @ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme). If the usage of Context is not related to display specific configuration or UI, then there is technically no need to use the annotation, and you can annotate the class with @SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="    private val sysuiContext: Context,"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt"
-            line="72"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window should use ShadeDisplayAware-annotated ConfigurationController, as the shade might move between windows, and only @ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme). If the usage of ConfigurationController is not related to display specific configuration or UI, then there is technically no need to use the annotation, and you can annotate the class with @SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="    private val configurationController: ConfigurationController,"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt"
-            line="74"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only @ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme). If the usage of Context is not related to display specific configuration or UI, then there is technically no need to use the annotation, and you can annotate the class with @SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="            Context context,"
-        errorLine2="                    ~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/biometrics/BiometricNotificationBroadcastReceiver.java"
-            line="46"
-            column="21"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only @ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme). If the usage of Resources is not related to display specific configuration or UI, then there is technically no need to use the annotation, and you can annotate the class with @SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="            @Main Resources resources,"
-        errorLine2="                            ~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/biometrics/BiometricNotificationDialogFactory.java"
-            line="52"
-            column="29"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only @ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme). If the usage of Context is not related to display specific configuration or UI, then there is technically no need to use the annotation, and you can annotate the class with @SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="    public BiometricNotificationService(@NonNull Context context,"
-        errorLine2="                                                         ~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/biometrics/BiometricNotificationService.java"
-            line="148"
-            column="58"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only @ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme). If the usage of Context is not related to display specific configuration or UI, then there is technically no need to use the annotation, and you can annotate the class with @SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="    c: Context,"
-        errorLine2="    ~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt"
-            line="61"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only @ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme). If the usage of Resources is not related to display specific configuration or UI, then there is technically no need to use the annotation, and you can annotate the class with @SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="    @Main private val resources: Resources,"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/EmergencyServicesRepository.kt"
-            line="39"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only @ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme). If the usage of Resources is not related to display specific configuration or UI, then there is technically no need to use the annotation, and you can annotate the class with @SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="    @Main private val resources: Resources,"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/biometrics/FaceAuthAccessibilityDelegate.kt"
-            line="37"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only @ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme). If the usage of Resources is not related to display specific configuration or UI, then there is technically no need to use the annotation, and you can annotate the class with @SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="    @Main private val resources: Resources,"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/biometrics/FaceHelpMessageDeferral.kt"
-            line="42"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only @ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme). If the usage of Context is not related to display specific configuration or UI, then there is technically no need to use the annotation, and you can annotate the class with @SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="            @NonNull final Context context,"
-        errorLine2="                                   ~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImpl.java"
-            line="186"
-            column="36"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only @ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme). If the usage of Context is not related to display specific configuration or UI, then there is technically no need to use the annotation, and you can annotate the class with @SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="class IconBuilder @Inject constructor(private val context: Context) {"
-        errorLine2="                                      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconBuilder.kt"
-            line="27"
-            column="39"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window should use ShadeDisplayAware-annotated WindowManager, as the shade might move between windows, and only @ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme). If the usage of WindowManager is not related to display specific configuration or UI, then there is technically no need to use the annotation, and you can annotate the class with @SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="    private val windowManager: WindowManager,"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/keyboard/docking/ui/viewmodel/KeyboardDockingIndicationViewModel.kt"
-            line="40"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only @ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme). If the usage of Resources is not related to display specific configuration or UI, then there is technically no need to use the annotation, and you can annotate the class with @SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="    @Main resources: Resources,"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModel.kt"
-            line="53"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only @ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme). If the usage of Resources is not related to display specific configuration or UI, then there is technically no need to use the annotation, and you can annotate the class with @SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="    @Main private val resources: Resources,"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/SafetyCenterAutoAddable.kt"
-            line="51"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only @ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme). If the usage of Context is not related to display specific configuration or UI, then there is technically no need to use the annotation, and you can annotate the class with @SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="    private val context: Context,"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractor.kt"
-            line="51"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="ShadeDisplayAwareContextChecker"
-        message="UI elements of the shade window should use ShadeDisplayAware-annotated WindowManager, as the shade might move between windows, and only @ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme). If the usage of WindowManager is not related to display specific configuration or UI, then there is technically no need to use the annotation, and you can annotate the class with @SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
-        errorLine1="    windowManager: WindowManager,"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractor.kt"
-            line="53"
-            column="5"/>
-    </issue>
-
 </issues>
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java
deleted file mode 100644
index ce57fe2..0000000
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java
+++ /dev/null
@@ -1,227 +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.keyguard;
-
-import static android.view.View.INVISIBLE;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.atLeast;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.content.res.Resources;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.FrameLayout;
-import android.widget.LinearLayout;
-import android.widget.RelativeLayout;
-
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FakeFeatureFlags;
-import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
-import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor;
-import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory;
-import com.android.systemui.keyguard.ui.view.InWindowLauncherUnlockAnimationManager;
-import com.android.systemui.log.LogBuffer;
-import com.android.systemui.plugins.clocks.ClockAnimations;
-import com.android.systemui.plugins.clocks.ClockController;
-import com.android.systemui.plugins.clocks.ClockEvents;
-import com.android.systemui.plugins.clocks.ClockFaceConfig;
-import com.android.systemui.plugins.clocks.ClockFaceController;
-import com.android.systemui.plugins.clocks.ClockFaceEvents;
-import com.android.systemui.plugins.clocks.ClockTickRate;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.res.R;
-import com.android.systemui.shared.clocks.AnimatableClockView;
-import com.android.systemui.shared.clocks.ClockRegistry;
-import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController;
-import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerAlwaysOnDisplayViewBinder;
-import com.android.systemui.statusbar.phone.NotificationIconContainer;
-import com.android.systemui.util.concurrency.FakeExecutor;
-import com.android.systemui.util.settings.SecureSettings;
-import com.android.systemui.util.time.FakeSystemClock;
-
-import org.junit.Before;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-public class KeyguardClockSwitchControllerBaseTest extends SysuiTestCase {
-
-    @Mock
-    protected KeyguardClockSwitch mView;
-    @Mock
-    protected StatusBarStateController mStatusBarStateController;
-    @Mock
-    protected ClockRegistry mClockRegistry;
-    @Mock
-    KeyguardSliceViewController mKeyguardSliceViewController;
-    @Mock
-    LockscreenSmartspaceController mSmartspaceController;
-
-    @Mock
-    Resources mResources;
-    @Mock
-    KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
-    @Mock
-    protected ClockController mClockController;
-    @Mock
-    protected ClockFaceController mLargeClockController;
-    @Mock
-    protected ClockFaceController mSmallClockController;
-    @Mock
-    protected ClockAnimations mClockAnimations;
-    @Mock
-    protected ClockEvents mClockEvents;
-    @Mock
-    protected ClockFaceEvents mClockFaceEvents;
-    @Mock
-    DumpManager mDumpManager;
-    @Mock
-    ClockEventController mClockEventController;
-
-    @Mock
-    protected NotificationIconContainer mNotificationIcons;
-    @Mock
-    protected AnimatableClockView mSmallClockView;
-    @Mock
-    protected AnimatableClockView mLargeClockView;
-    @Mock
-    protected FrameLayout mSmallClockFrame;
-    @Mock
-    protected FrameLayout mLargeClockFrame;
-    @Mock
-    protected SecureSettings mSecureSettings;
-    @Mock
-    protected LogBuffer mLogBuffer;
-
-    @Mock
-    protected KeyguardClockInteractor mKeyguardClockInteractor;
-
-    protected final View mFakeDateView = (View) (new ViewGroup(mContext) {
-        @Override
-        protected void onLayout(boolean changed, int l, int t, int r, int b) {}
-    });
-    protected final View mFakeWeatherView = new View(mContext);
-    protected final View mFakeSmartspaceView = new View(mContext);
-
-    protected KeyguardClockSwitchController mController;
-    protected View mSliceView;
-    protected LinearLayout mStatusArea;
-    protected FakeExecutor mExecutor;
-    protected FakeFeatureFlags mFakeFeatureFlags;
-    @Captor protected ArgumentCaptor<View.OnAttachStateChangeListener> mAttachCaptor =
-            ArgumentCaptor.forClass(View.OnAttachStateChangeListener.class);
-
-    @Before
-    public void setup() {
-        MockitoAnnotations.initMocks(this);
-
-        mFakeDateView.setTag(R.id.tag_smartspace_view, new Object());
-        mFakeWeatherView.setTag(R.id.tag_smartspace_view, new Object());
-        mFakeSmartspaceView.setTag(R.id.tag_smartspace_view, new Object());
-
-        when(mView.findViewById(R.id.left_aligned_notification_icon_container))
-                .thenReturn(mNotificationIcons);
-        when(mNotificationIcons.getLayoutParams()).thenReturn(
-                mock(RelativeLayout.LayoutParams.class));
-        when(mView.getContext()).thenReturn(getContext());
-        when(mView.getResources()).thenReturn(mResources);
-        when(mResources.getDimensionPixelSize(R.dimen.keyguard_clock_top_margin))
-                .thenReturn(100);
-        when(mResources.getDimensionPixelSize(com.android.systemui.customization.R.dimen.keyguard_large_clock_top_margin))
-                .thenReturn(-200);
-        when(mResources.getInteger(com.android.internal.R.integer.config_doublelineClockDefault))
-                .thenReturn(1);
-        when(mResources.getInteger(R.integer.keyguard_date_weather_view_invisibility))
-                .thenReturn(INVISIBLE);
-
-        when(mView
-                .findViewById(com.android.systemui.customization.R.id.lockscreen_clock_view_large))
-                .thenReturn(mLargeClockFrame);
-        when(mView
-                .findViewById(com.android.systemui.customization.R.id.lockscreen_clock_view))
-                .thenReturn(mSmallClockFrame);
-        when(mSmallClockView.getContext()).thenReturn(getContext());
-        when(mLargeClockView.getContext()).thenReturn(getContext());
-
-        when(mView.isAttachedToWindow()).thenReturn(true);
-        when(mSmartspaceController.buildAndConnectDateView(any())).thenReturn(mFakeDateView);
-        when(mSmartspaceController.buildAndConnectWeatherView(any())).thenReturn(mFakeWeatherView);
-        when(mSmartspaceController.buildAndConnectView(any())).thenReturn(mFakeSmartspaceView);
-        mExecutor = new FakeExecutor(new FakeSystemClock());
-        mFakeFeatureFlags = new FakeFeatureFlags();
-        mController = new KeyguardClockSwitchController(
-                mView,
-                mStatusBarStateController,
-                mClockRegistry,
-                mKeyguardSliceViewController,
-                mSmartspaceController,
-                mock(NotificationIconContainerAlwaysOnDisplayViewBinder.class),
-                mKeyguardUnlockAnimationController,
-                mSecureSettings,
-                mExecutor,
-                mExecutor,
-                mDumpManager,
-                mClockEventController,
-                mLogBuffer,
-                KeyguardInteractorFactory.create(mFakeFeatureFlags).getKeyguardInteractor(),
-                mKeyguardClockInteractor,
-                mock(InWindowLauncherUnlockAnimationManager.class)
-        );
-
-        when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE);
-        when(mLargeClockController.getView()).thenReturn(mLargeClockView);
-        when(mSmallClockController.getView()).thenReturn(mSmallClockView);
-        when(mClockController.getLargeClock()).thenReturn(mLargeClockController);
-        when(mClockController.getSmallClock()).thenReturn(mSmallClockController);
-        when(mClockController.getEvents()).thenReturn(mClockEvents);
-        when(mSmallClockController.getEvents()).thenReturn(mClockFaceEvents);
-        when(mLargeClockController.getEvents()).thenReturn(mClockFaceEvents);
-        when(mLargeClockController.getAnimations()).thenReturn(mClockAnimations);
-        when(mSmallClockController.getAnimations()).thenReturn(mClockAnimations);
-        when(mClockRegistry.createCurrentClock()).thenReturn(mClockController);
-        when(mClockEventController.getClock()).thenReturn(mClockController);
-        when(mSmallClockController.getConfig())
-                .thenReturn(new ClockFaceConfig(ClockTickRate.PER_MINUTE, false, false, false));
-        when(mLargeClockController.getConfig())
-                .thenReturn(new ClockFaceConfig(ClockTickRate.PER_MINUTE, false, false, false));
-
-        mSliceView = new View(getContext());
-        when(mView.findViewById(R.id.keyguard_slice_view)).thenReturn(mSliceView);
-        mStatusArea = new LinearLayout(getContext());
-        when(mView.findViewById(R.id.keyguard_status_area)).thenReturn(mStatusArea);
-    }
-
-    private void removeView(View v) {
-        ViewGroup group = ((ViewGroup) v.getParent());
-        if (group != null) {
-            group.removeView(v);
-        }
-    }
-
-    protected void init() {
-        mController.init();
-
-        verify(mView, atLeast(1)).addOnAttachStateChangeListener(mAttachCaptor.capture());
-        mAttachCaptor.getValue().onViewAttachedToWindow(mView);
-    }
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
deleted file mode 100644
index 892375d..0000000
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
+++ /dev/null
@@ -1,278 +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.keyguard;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.database.ContentObserver;
-import android.os.UserHandle;
-import android.platform.test.annotations.DisableFlags;
-import android.provider.Settings;
-import android.view.View;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.Flags;
-import com.android.systemui.plugins.clocks.ClockFaceConfig;
-import com.android.systemui.plugins.clocks.ClockTickRate;
-import com.android.systemui.shared.clocks.ClockRegistry;
-import com.android.systemui.statusbar.StatusBarState;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.verification.VerificationMode;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-@DisableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
-public class KeyguardClockSwitchControllerTest extends KeyguardClockSwitchControllerBaseTest {
-    @Test
-    public void testInit_viewAlreadyAttached() {
-        mController.init();
-
-        verifyAttachment(times(1));
-    }
-
-    @Test
-    public void testInit_viewNotYetAttached() {
-        ArgumentCaptor<View.OnAttachStateChangeListener> listenerArgumentCaptor =
-                ArgumentCaptor.forClass(View.OnAttachStateChangeListener.class);
-
-        when(mView.isAttachedToWindow()).thenReturn(false);
-        mController.init();
-        verify(mView).addOnAttachStateChangeListener(listenerArgumentCaptor.capture());
-
-        verifyAttachment(never());
-
-        listenerArgumentCaptor.getValue().onViewAttachedToWindow(mView);
-
-        verifyAttachment(times(1));
-    }
-
-    @Test
-    public void testInitSubControllers() {
-        mController.init();
-        verify(mKeyguardSliceViewController).init();
-    }
-
-    @Test
-    public void testInit_viewDetached() {
-        ArgumentCaptor<View.OnAttachStateChangeListener> listenerArgumentCaptor =
-                ArgumentCaptor.forClass(View.OnAttachStateChangeListener.class);
-        mController.init();
-        verify(mView).addOnAttachStateChangeListener(listenerArgumentCaptor.capture());
-
-        verifyAttachment(times(1));
-
-        listenerArgumentCaptor.getValue().onViewDetachedFromWindow(mView);
-        verify(mClockEventController).unregisterListeners();
-    }
-
-    @Test
-    public void testPluginPassesStatusBarState() {
-        ArgumentCaptor<ClockRegistry.ClockChangeListener> listenerArgumentCaptor =
-                ArgumentCaptor.forClass(ClockRegistry.ClockChangeListener.class);
-
-        mController.init();
-        verify(mClockRegistry).registerClockChangeListener(listenerArgumentCaptor.capture());
-
-        listenerArgumentCaptor.getValue().onCurrentClockChanged();
-        verify(mView, times(2)).setClock(mClockController, StatusBarState.SHADE);
-        verify(mClockEventController, times(2)).setClock(mClockController);
-    }
-
-    @Test
-    public void testSmartspaceEnabledRemovesKeyguardStatusArea() {
-        when(mSmartspaceController.isEnabled()).thenReturn(true);
-        mController.init();
-
-        assertEquals(View.GONE, mSliceView.getVisibility());
-    }
-
-    @Test
-    public void onLocaleListChangedRebuildsSmartspaceView() {
-        when(mSmartspaceController.isEnabled()).thenReturn(true);
-        mController.init();
-
-        mController.onLocaleListChanged();
-        // Should be called once on initial setup, then once again for locale change
-        verify(mSmartspaceController, times(2)).buildAndConnectView(mView);
-    }
-
-    @Test
-    public void onLocaleListChanged_rebuildsSmartspaceViews_whenDecouplingEnabled() {
-        when(mSmartspaceController.isEnabled()).thenReturn(true);
-        when(mSmartspaceController.isDateWeatherDecoupled()).thenReturn(true);
-        mController.init();
-
-        mController.onLocaleListChanged();
-        // Should be called once on initial setup, then once again for locale change
-        verify(mSmartspaceController, times(2)).buildAndConnectDateView(mView);
-        verify(mSmartspaceController, times(2)).buildAndConnectWeatherView(mView);
-        verify(mSmartspaceController, times(2)).buildAndConnectView(mView);
-    }
-
-    @Test
-    public void testSmartspaceDisabledShowsKeyguardStatusArea() {
-        when(mSmartspaceController.isEnabled()).thenReturn(false);
-        mController.init();
-
-        assertEquals(View.VISIBLE, mSliceView.getVisibility());
-    }
-
-    @Test
-    public void testRefresh() {
-        mController.refresh();
-
-        verify(mSmartspaceController).requestSmartspaceUpdate();
-    }
-
-    @Test
-    public void testChangeToDoubleLineClockSetsSmallClock() {
-        when(mSecureSettings.getIntForUser(Settings.Secure.LOCKSCREEN_USE_DOUBLE_LINE_CLOCK, 1,
-                UserHandle.USER_CURRENT))
-                .thenReturn(0);
-        ArgumentCaptor<ContentObserver> observerCaptor =
-                ArgumentCaptor.forClass(ContentObserver.class);
-        mController.init();
-        mExecutor.runAllReady();
-        verify(mSecureSettings).registerContentObserverForUserSync(
-                eq(Settings.Secure.LOCKSCREEN_USE_DOUBLE_LINE_CLOCK),
-                    anyBoolean(), observerCaptor.capture(), eq(UserHandle.USER_ALL));
-        ContentObserver observer = observerCaptor.getValue();
-        mExecutor.runAllReady();
-
-        // When a settings change has occurred to the small clock, make sure the view is adjusted
-        reset(mView);
-        when(mView.getResources()).thenReturn(mResources);
-        observer.onChange(true);
-        mExecutor.runAllReady();
-        verify(mView).switchToClock(KeyguardClockSwitch.SMALL, /* animate */ true);
-    }
-
-    @Test
-    public void testGetClock_ForwardsToClock() {
-        assertEquals(mClockController, mController.getClock());
-    }
-
-    @Test
-    public void testGetLargeClockBottom_returnsExpectedValue() {
-        when(mLargeClockFrame.getVisibility()).thenReturn(View.VISIBLE);
-        when(mLargeClockFrame.getHeight()).thenReturn(100);
-        when(mSmallClockFrame.getHeight()).thenReturn(50);
-        when(mLargeClockView.getHeight()).thenReturn(40);
-        when(mSmallClockView.getHeight()).thenReturn(20);
-        mController.init();
-
-        assertEquals(170, mController.getClockBottom(1000));
-    }
-
-    @Test
-    public void testGetSmallLargeClockBottom_returnsExpectedValue() {
-        when(mLargeClockFrame.getVisibility()).thenReturn(View.GONE);
-        when(mLargeClockFrame.getHeight()).thenReturn(100);
-        when(mSmallClockFrame.getHeight()).thenReturn(50);
-        when(mLargeClockView.getHeight()).thenReturn(40);
-        when(mSmallClockView.getHeight()).thenReturn(20);
-        mController.init();
-
-        assertEquals(1120, mController.getClockBottom(1000));
-    }
-
-    @Test
-    public void testGetClockBottom_nullClock_returnsZero() {
-        when(mClockEventController.getClock()).thenReturn(null);
-        assertEquals(0, mController.getClockBottom(10));
-    }
-
-    @Test
-    public void testChangeLockscreenWeatherEnabledSetsWeatherViewVisible() {
-        when(mSmartspaceController.isWeatherEnabled()).thenReturn(true);
-        ArgumentCaptor<ContentObserver> observerCaptor =
-                ArgumentCaptor.forClass(ContentObserver.class);
-        mController.init();
-        mExecutor.runAllReady();
-        verify(mSecureSettings).registerContentObserverForUserSync(
-                eq(Settings.Secure.LOCK_SCREEN_WEATHER_ENABLED), anyBoolean(),
-                    observerCaptor.capture(), eq(UserHandle.USER_ALL));
-        ContentObserver observer = observerCaptor.getValue();
-        mExecutor.runAllReady();
-        // When a settings change has occurred, check that view is visible.
-        observer.onChange(true);
-        mExecutor.runAllReady();
-        assertEquals(View.VISIBLE, mFakeWeatherView.getVisibility());
-    }
-
-    @Test
-    public void testChangeClockDateWeatherEnabled_SetsDateWeatherViewVisibility() {
-        ArgumentCaptor<ClockRegistry.ClockChangeListener> listenerArgumentCaptor =
-                ArgumentCaptor.forClass(ClockRegistry.ClockChangeListener.class);
-        when(mSmartspaceController.isEnabled()).thenReturn(true);
-        when(mSmartspaceController.isDateWeatherDecoupled()).thenReturn(true);
-        when(mSmartspaceController.isWeatherEnabled()).thenReturn(true);
-        mController.init();
-        mExecutor.runAllReady();
-        assertEquals(View.VISIBLE, mFakeDateView.getVisibility());
-
-        when(mSmallClockController.getConfig())
-                .thenReturn(new ClockFaceConfig(ClockTickRate.PER_MINUTE, true, false, true));
-        when(mLargeClockController.getConfig())
-                .thenReturn(new ClockFaceConfig(ClockTickRate.PER_MINUTE, true, false, true));
-        verify(mClockRegistry).registerClockChangeListener(listenerArgumentCaptor.capture());
-        listenerArgumentCaptor.getValue().onCurrentClockChanged();
-
-        mExecutor.runAllReady();
-        assertEquals(View.INVISIBLE, mFakeDateView.getVisibility());
-    }
-
-    @Test
-    public void testGetClock_nullClock_returnsNull() {
-        when(mClockEventController.getClock()).thenReturn(null);
-        assertNull(mController.getClock());
-    }
-
-    private void verifyAttachment(VerificationMode times) {
-        verify(mClockRegistry, times).registerClockChangeListener(
-                any(ClockRegistry.ClockChangeListener.class));
-        verify(mClockEventController, times).registerListeners(mView);
-    }
-
-    @Test
-    public void testSplitShadeEnabledSetToSmartspaceController() {
-        mController.setSplitShadeEnabled(true);
-        verify(mSmartspaceController, times(1)).setSplitShadeEnabled(true);
-        verify(mSmartspaceController, times(0)).setSplitShadeEnabled(false);
-    }
-
-    @Test
-    public void testSplitShadeDisabledSetToSmartspaceController() {
-        mController.setSplitShadeEnabled(false);
-        verify(mSmartspaceController, times(1)).setSplitShadeEnabled(false);
-        verify(mSmartspaceController, times(0)).setSplitShadeEnabled(true);
-    }
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardClockSwitchTest.java b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardClockSwitchTest.java
deleted file mode 100644
index 4ed5fd0..0000000
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardClockSwitchTest.java
+++ /dev/null
@@ -1,273 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.keyguard;
-
-import static android.view.View.INVISIBLE;
-import static android.view.View.VISIBLE;
-
-import static com.android.keyguard.KeyguardClockSwitch.LARGE;
-import static com.android.keyguard.KeyguardClockSwitch.SMALL;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static junit.framework.TestCase.assertEquals;
-
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import android.content.Context;
-import android.platform.test.annotations.DisableFlags;
-import android.testing.TestableLooper.RunWithLooper;
-import android.util.AttributeSet;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.FrameLayout;
-import android.widget.TextView;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.Flags;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.plugins.clocks.ClockController;
-import com.android.systemui.plugins.clocks.ClockFaceController;
-import com.android.systemui.res.R;
-import com.android.systemui.statusbar.StatusBarState;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-// Need to run on the main thread because KeyguardSliceView$Row init checks for
-// the main thread before acquiring a wake lock. This class is constructed when
-// the keyguard_clock_switch layout is inflated.
-@RunWithLooper(setAsMainLooper = true)
-@DisableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
-public class KeyguardClockSwitchTest extends SysuiTestCase {
-    @Mock
-    ViewGroup mMockKeyguardSliceView;
-
-    @Mock
-    ClockController mClock;
-
-    @Mock
-    ClockFaceController mSmallClock;
-
-    @Mock
-    ClockFaceController mLargeClock;
-
-    private FrameLayout mSmallClockFrame;
-    private FrameLayout mLargeClockFrame;
-    private KeyguardStatusAreaView mStatusArea;
-
-    KeyguardClockSwitch mKeyguardClockSwitch;
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-        when(mMockKeyguardSliceView.getContext()).thenReturn(mContext);
-        when(mMockKeyguardSliceView.findViewById(R.id.keyguard_status_area))
-                .thenReturn(mMockKeyguardSliceView);
-
-        when(mClock.getSmallClock()).thenReturn(mSmallClock);
-        when(mClock.getLargeClock()).thenReturn(mLargeClock);
-
-        when(mSmallClock.getView()).thenReturn(new TextView(getContext()));
-        when(mLargeClock.getView()).thenReturn(new TextView(getContext()));
-
-        LayoutInflater layoutInflater = LayoutInflater.from(getContext());
-        layoutInflater.setPrivateFactory(new LayoutInflater.Factory2() {
-
-            @Override
-            public View onCreateView(View parent, String name, Context context,
-                    AttributeSet attrs) {
-                return onCreateView(name, context, attrs);
-            }
-
-            @Override
-            public View onCreateView(String name, Context context, AttributeSet attrs) {
-                if ("com.android.keyguard.KeyguardSliceView".equals(name)) {
-                    return mMockKeyguardSliceView;
-                }
-                return null;
-            }
-        });
-        mKeyguardClockSwitch =
-                (KeyguardClockSwitch) layoutInflater.inflate(R.layout.keyguard_clock_switch, null);
-        mSmallClockFrame = mKeyguardClockSwitch
-                .findViewById(com.android.systemui.customization.R.id.lockscreen_clock_view);
-        mLargeClockFrame = mKeyguardClockSwitch
-                .findViewById(com.android.systemui.customization.R.id.lockscreen_clock_view_large);
-        mStatusArea = mKeyguardClockSwitch.findViewById(R.id.keyguard_status_area);
-        mKeyguardClockSwitch.mChildrenAreLaidOut = true;
-    }
-
-    @Test
-    public void noPluginConnected_showNothing() {
-        mKeyguardClockSwitch.setClock(null, StatusBarState.KEYGUARD);
-        assertEquals(mLargeClockFrame.getChildCount(), 0);
-        assertEquals(mSmallClockFrame.getChildCount(), 0);
-    }
-
-    @Test
-    public void pluginConnectedThenDisconnected_showNothing() {
-        mKeyguardClockSwitch.setClock(mClock, StatusBarState.KEYGUARD);
-        assertEquals(mLargeClockFrame.getChildCount(), 1);
-        assertEquals(mSmallClockFrame.getChildCount(), 1);
-
-        mKeyguardClockSwitch.setClock(null, StatusBarState.KEYGUARD);
-        assertEquals(mLargeClockFrame.getChildCount(), 0);
-        assertEquals(mSmallClockFrame.getChildCount(), 0);
-    }
-
-    @Test
-    public void onPluginConnected_showClock() {
-        mKeyguardClockSwitch.setClock(mClock, StatusBarState.KEYGUARD);
-
-        assertEquals(mClock.getSmallClock().getView().getParent(), mSmallClockFrame);
-        assertEquals(mClock.getLargeClock().getView().getParent(), mLargeClockFrame);
-    }
-
-    @Test
-    public void onPluginConnected_showSecondPluginClock() {
-        // GIVEN a plugin has already connected
-        ClockController otherClock = mock(ClockController.class);
-        ClockFaceController smallClock = mock(ClockFaceController.class);
-        ClockFaceController largeClock = mock(ClockFaceController.class);
-        when(otherClock.getSmallClock()).thenReturn(smallClock);
-        when(otherClock.getLargeClock()).thenReturn(largeClock);
-        when(smallClock.getView()).thenReturn(new TextView(getContext()));
-        when(largeClock.getView()).thenReturn(new TextView(getContext()));
-        mKeyguardClockSwitch.setClock(mClock, StatusBarState.KEYGUARD);
-        mKeyguardClockSwitch.setClock(otherClock, StatusBarState.KEYGUARD);
-
-        // THEN only the view from the second plugin should be a child of KeyguardClockSwitch.
-        assertThat(otherClock.getSmallClock().getView().getParent()).isEqualTo(mSmallClockFrame);
-        assertThat(otherClock.getLargeClock().getView().getParent()).isEqualTo(mLargeClockFrame);
-        assertThat(mClock.getSmallClock().getView().getParent()).isNull();
-        assertThat(mClock.getLargeClock().getView().getParent()).isNull();
-    }
-
-    @Test
-    public void onPluginDisconnected_secondOfTwoDisconnected() {
-        // GIVEN two plugins are connected
-        ClockController otherClock = mock(ClockController.class);
-        ClockFaceController smallClock = mock(ClockFaceController.class);
-        ClockFaceController largeClock = mock(ClockFaceController.class);
-        when(otherClock.getSmallClock()).thenReturn(smallClock);
-        when(otherClock.getLargeClock()).thenReturn(largeClock);
-        when(smallClock.getView()).thenReturn(new TextView(getContext()));
-        when(largeClock.getView()).thenReturn(new TextView(getContext()));
-        mKeyguardClockSwitch.setClock(otherClock, StatusBarState.KEYGUARD);
-        mKeyguardClockSwitch.setClock(mClock, StatusBarState.KEYGUARD);
-        // WHEN the second plugin is disconnected
-        mKeyguardClockSwitch.setClock(null, StatusBarState.KEYGUARD);
-        // THEN nothing should be shown
-        assertThat(otherClock.getSmallClock().getView().getParent()).isNull();
-        assertThat(otherClock.getLargeClock().getView().getParent()).isNull();
-        assertThat(mClock.getSmallClock().getView().getParent()).isNull();
-        assertThat(mClock.getLargeClock().getView().getParent()).isNull();
-    }
-
-    @Test
-    public void switchingToBigClockWithAnimation_makesSmallClockDisappear() {
-        mKeyguardClockSwitch.switchToClock(LARGE, /* animate */ true);
-
-        mKeyguardClockSwitch.mClockInAnim.end();
-        mKeyguardClockSwitch.mClockOutAnim.end();
-        mKeyguardClockSwitch.mStatusAreaAnim.end();
-
-        assertThat(mLargeClockFrame.getAlpha()).isEqualTo(1);
-        assertThat(mLargeClockFrame.getVisibility()).isEqualTo(VISIBLE);
-        assertThat(mSmallClockFrame.getAlpha()).isEqualTo(0);
-        assertThat(mSmallClockFrame.getVisibility()).isEqualTo(INVISIBLE);
-    }
-
-    @Test
-    public void switchingToBigClockNoAnimation_makesSmallClockDisappear() {
-        mKeyguardClockSwitch.switchToClock(LARGE, /* animate */ false);
-
-        assertThat(mLargeClockFrame.getAlpha()).isEqualTo(1);
-        assertThat(mLargeClockFrame.getVisibility()).isEqualTo(VISIBLE);
-        assertThat(mSmallClockFrame.getAlpha()).isEqualTo(0);
-        assertThat(mSmallClockFrame.getVisibility()).isEqualTo(INVISIBLE);
-    }
-
-    @Test
-    public void switchingToSmallClockWithAnimation_makesBigClockDisappear() {
-        mKeyguardClockSwitch.switchToClock(SMALL, /* animate */ true);
-
-        mKeyguardClockSwitch.mClockInAnim.end();
-        mKeyguardClockSwitch.mClockOutAnim.end();
-        mKeyguardClockSwitch.mStatusAreaAnim.end();
-
-        assertThat(mSmallClockFrame.getAlpha()).isEqualTo(1);
-        assertThat(mSmallClockFrame.getVisibility()).isEqualTo(VISIBLE);
-        // only big clock is removed at switch
-        assertThat(mLargeClockFrame.getParent()).isNull();
-        assertThat(mLargeClockFrame.getAlpha()).isEqualTo(0);
-        assertThat(mLargeClockFrame.getVisibility()).isEqualTo(INVISIBLE);
-    }
-
-    @Test
-    public void switchingToSmallClockNoAnimation_makesBigClockDisappear() {
-        mKeyguardClockSwitch.switchToClock(SMALL, false);
-
-        assertThat(mSmallClockFrame.getAlpha()).isEqualTo(1);
-        assertThat(mSmallClockFrame.getVisibility()).isEqualTo(VISIBLE);
-        // only big clock is removed at switch
-        assertThat(mLargeClockFrame.getParent()).isNull();
-        assertThat(mLargeClockFrame.getAlpha()).isEqualTo(0);
-        assertThat(mLargeClockFrame.getVisibility()).isEqualTo(INVISIBLE);
-    }
-
-    @Test
-    public void switchingToSmallClockAnimation_resetsStatusArea() {
-        mKeyguardClockSwitch.switchToClock(SMALL, true);
-
-        mKeyguardClockSwitch.mClockInAnim.end();
-        mKeyguardClockSwitch.mClockOutAnim.end();
-        mKeyguardClockSwitch.mStatusAreaAnim.end();
-
-        assertThat(mStatusArea.getTranslationX()).isEqualTo(0);
-        assertThat(mStatusArea.getTranslationY()).isEqualTo(0);
-        assertThat(mStatusArea.getScaleX()).isEqualTo(1);
-        assertThat(mStatusArea.getScaleY()).isEqualTo(1);
-    }
-
-    @Test
-    public void switchingToSmallClockNoAnimation_resetsStatusArea() {
-        mKeyguardClockSwitch.switchToClock(SMALL, false);
-
-        assertThat(mStatusArea.getTranslationX()).isEqualTo(0);
-        assertThat(mStatusArea.getTranslationY()).isEqualTo(0);
-        assertThat(mStatusArea.getScaleX()).isEqualTo(1);
-        assertThat(mStatusArea.getScaleY()).isEqualTo(1);
-    }
-
-
-    @Test
-    public void switchingToBigClock_returnsTrueOnlyWhenItWasNotVisibleBefore() {
-        assertThat(mKeyguardClockSwitch.switchToClock(LARGE, /* animate */ true)).isTrue();
-        assertThat(mKeyguardClockSwitch.switchToClock(LARGE, /* animate */ true)).isFalse();
-    }
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationControllerTest.java
index fa8cdcc..80de087 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationControllerTest.java
@@ -24,6 +24,7 @@
 import android.platform.test.annotations.EnableFlags;
 import android.testing.TestableLooper;
 import android.view.WindowManager;
+import android.view.accessibility.AccessibilityManager;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
@@ -39,6 +40,7 @@
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mock;
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
 
@@ -54,11 +56,15 @@
     @Rule
     public MockitoRule mockito = MockitoJUnit.rule();
 
+    @Mock
+    private AccessibilityManager mAccessibilityManager;
+
     @Before
     public void setUp() throws Exception {
         final WindowManager stubWindowManager = mContext.getSystemService(WindowManager.class);
         final SecureSettings mockSecureSettings = TestUtils.mockSecureSettings();
-        final MenuViewModel stubMenuViewModel = new MenuViewModel(mContext, mockSecureSettings);
+        final MenuViewModel stubMenuViewModel = new MenuViewModel(mContext, mAccessibilityManager,
+                mockSecureSettings);
         final MenuViewAppearance stubMenuViewAppearance = new MenuViewAppearance(mContext,
                 stubWindowManager);
         final MenuView stubMenuView = spy(new MenuView(mContext, stubMenuViewModel,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepositoryTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepositoryTest.java
index 7e4b6f9..24f3a29 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepositoryTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepositoryTest.java
@@ -16,11 +16,16 @@
 
 package com.android.systemui.accessibility.floatingmenu;
 
+import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME;
+
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.verify;
 
+import android.content.Context;
 import android.content.res.Configuration;
+import android.view.accessibility.AccessibilityManager;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
@@ -28,6 +33,7 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.util.settings.SecureSettings;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -36,6 +42,8 @@
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
 
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Locale;
 
 /** Tests for {@link MenuInfoRepository}. */
@@ -46,16 +54,30 @@
     public MockitoRule mockito = MockitoJUnit.rule();
 
     @Mock
+    private AccessibilityManager mAccessibilityManager;
+
+    @Mock
     private MenuInfoRepository.OnSettingsContentsChanged mMockSettingsContentsChanged;
     @Mock
     private SecureSettings mSecureSettings;
 
     private MenuInfoRepository mMenuInfoRepository;
+    private final List<String> mShortcutTargets = new ArrayList<>();
 
     @Before
     public void setUp() {
-        mMenuInfoRepository = new MenuInfoRepository(mContext, mMockSettingsContentsChanged,
-                mSecureSettings);
+        mContext.addMockSystemService(Context.ACCESSIBILITY_SERVICE, mAccessibilityManager);
+        mShortcutTargets.add(MAGNIFICATION_CONTROLLER_NAME);
+        doReturn(mShortcutTargets).when(mAccessibilityManager).getAccessibilityShortcutTargets(
+                anyInt());
+
+        mMenuInfoRepository = new MenuInfoRepository(mContext, mAccessibilityManager,
+                mMockSettingsContentsChanged, mSecureSettings);
+    }
+
+    @After
+    public void tearDown() {
+        mShortcutTargets.clear();
     }
 
     @Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java
index 1f48bec..157cccc 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java
@@ -83,7 +83,8 @@
         final WindowManager stubWindowManager = mContext.getSystemService(WindowManager.class);
         final MenuViewAppearance stubMenuViewAppearance = new MenuViewAppearance(mContext,
                 stubWindowManager);
-        final MenuViewModel stubMenuViewModel = new MenuViewModel(mContext, mSecureSettings);
+        final MenuViewModel stubMenuViewModel = new MenuViewModel(mContext, mAccessibilityManager,
+                mSecureSettings);
 
         final int halfScreenHeight =
                 stubWindowManager.getCurrentWindowMetrics().getBounds().height() / 2;
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java
index f7b81cc..46f076a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java
@@ -33,6 +33,7 @@
 import android.testing.TestableLooper;
 import android.view.MotionEvent;
 import android.view.WindowManager;
+import android.view.accessibility.AccessibilityManager;
 
 import androidx.dynamicanimation.animation.DynamicAnimation;
 import androidx.recyclerview.widget.RecyclerView;
@@ -52,6 +53,7 @@
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mock;
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
 
@@ -78,11 +80,15 @@
     @Rule
     public MockitoRule mockito = MockitoJUnit.rule();
 
+    @Mock
+    private AccessibilityManager mAccessibilityManager;
+
     @Before
     public void setUp() throws Exception {
         final WindowManager windowManager = mContext.getSystemService(WindowManager.class);
         final SecureSettings secureSettings = TestUtils.mockSecureSettings();
-        final MenuViewModel stubMenuViewModel = new MenuViewModel(mContext, secureSettings);
+        final MenuViewModel stubMenuViewModel = new MenuViewModel(mContext, mAccessibilityManager,
+                secureSettings);
         final MenuViewAppearance stubMenuViewAppearance = new MenuViewAppearance(mContext,
                 windowManager);
         mStubMenuView = new MenuView(mContext, stubMenuViewModel, stubMenuViewAppearance,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java
index c1708d1..ee8ce17 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java
@@ -32,6 +32,7 @@
 import android.platform.test.annotations.EnableFlags;
 import android.testing.TestableLooper;
 import android.view.WindowManager;
+import android.view.accessibility.AccessibilityManager;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
@@ -48,6 +49,7 @@
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mock;
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
 
@@ -66,6 +68,9 @@
     @Rule
     public MockitoRule mockito = MockitoJUnit.rule();
 
+    @Mock
+    private AccessibilityManager mAccessibilityManager;
+
     private SysuiTestableContext mSpyContext;
 
     @Before
@@ -84,7 +89,8 @@
         doNothing().when(mSpyContext).startActivity(any());
 
         final SecureSettings secureSettings = TestUtils.mockSecureSettings();
-        final MenuViewModel stubMenuViewModel = new MenuViewModel(mContext, secureSettings);
+        final MenuViewModel stubMenuViewModel = new MenuViewModel(mContext, mAccessibilityManager,
+                secureSettings);
         final WindowManager stubWindowManager = mContext.getSystemService(WindowManager.class);
         mStubMenuViewAppearance = new MenuViewAppearance(mSpyContext, stubWindowManager);
         mMenuView = spy(new MenuView(mSpyContext, stubMenuViewModel, mStubMenuViewAppearance,
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 ab93659..66f44ba 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
@@ -158,6 +158,22 @@
     private val mockFingerprintIconWidth = 300
     private val mockFingerprintIconHeight = 300
 
+    private val faceIconAuthingDescription =
+        R.string.biometric_dialog_face_icon_description_authenticating
+    private val faceIconAuthedDescription =
+        R.string.biometric_dialog_face_icon_description_authenticated
+    private val faceIconConfirmedDescription =
+        R.string.biometric_dialog_face_icon_description_confirmed
+    private val faceIconIdleDescription = R.string.biometric_dialog_face_icon_description_idle
+    private val sfpsFindSensorDescription =
+        R.string.security_settings_sfps_enroll_find_sensor_message
+    private val udfpsIconDescription = R.string.accessibility_fingerprint_label
+    private val faceFailedDescription = R.string.keyguard_face_failed
+    private val bpTryAgainDescription = R.string.biometric_dialog_try_again
+    private val bpConfirmDescription = R.string.biometric_dialog_confirm
+    private val fingerprintIconAuthenticatedDescription =
+        R.string.fingerprint_dialog_authenticated_confirmation
+
     /** Mock [UdfpsOverlayParams] for a test. */
     private fun mockUdfpsOverlayParams(isLandscape: Boolean = false): UdfpsOverlayParams =
         UdfpsOverlayParams(
@@ -337,21 +353,18 @@
             if ((testCase.isCoex && !forceExplicitFlow) || testCase.isFaceOnly) {
                 // Face-only or implicit co-ex auth
                 assertThat(iconAsset).isEqualTo(R.raw.face_dialog_authenticating)
-                assertThat(iconContentDescriptionId)
-                    .isEqualTo(R.string.biometric_dialog_face_icon_description_authenticating)
+                assertThat(iconContentDescriptionId).isEqualTo(faceIconAuthingDescription)
                 assertThat(shouldAnimateIconView).isEqualTo(true)
             } else if ((testCase.isCoex && forceExplicitFlow) || testCase.isFingerprintOnly) {
                 // Fingerprint-only or explicit co-ex auth
                 if (testCase.sensorType == FingerprintSensorProperties.TYPE_POWER_BUTTON) {
                     assertThat(iconAsset).isEqualTo(getSfpsAsset_fingerprintAuthenticating())
-                    assertThat(iconContentDescriptionId)
-                        .isEqualTo(R.string.security_settings_sfps_enroll_find_sensor_message)
+                    assertThat(iconContentDescriptionId).isEqualTo(sfpsFindSensorDescription)
                     assertThat(shouldAnimateIconView).isEqualTo(true)
                 } else {
                     assertThat(iconAsset)
                         .isEqualTo(R.raw.fingerprint_dialogue_fingerprint_to_error_lottie)
-                    assertThat(iconContentDescriptionId)
-                        .isEqualTo(R.string.fingerprint_dialog_touch_sensor)
+                    assertThat(iconContentDescriptionId).isEqualTo(udfpsIconDescription)
                     assertThat(shouldAnimateIconView).isEqualTo(false)
                 }
             }
@@ -397,26 +410,25 @@
         if (testCase.isFaceOnly) {
             // Face-only auth
             assertThat(iconAsset).isEqualTo(R.raw.face_dialog_dark_to_error)
-            assertThat(iconContentDescriptionId).isEqualTo(R.string.keyguard_face_failed)
+            assertThat(iconContentDescriptionId).isEqualTo(faceFailedDescription)
             assertThat(shouldAnimateIconView).isEqualTo(true)
 
             // Clear error, go to idle
             errorJob.join()
 
             assertThat(iconAsset).isEqualTo(R.raw.face_dialog_error_to_idle)
-            assertThat(iconContentDescriptionId)
-                .isEqualTo(R.string.biometric_dialog_face_icon_description_idle)
+            assertThat(iconContentDescriptionId).isEqualTo(faceIconIdleDescription)
             assertThat(shouldAnimateIconView).isEqualTo(true)
         } else if ((testCase.isCoex && forceExplicitFlow) || testCase.isFingerprintOnly) {
             // Fingerprint-only or explicit co-ex auth
             if (testCase.sensorType == FingerprintSensorProperties.TYPE_POWER_BUTTON) {
                 assertThat(iconAsset).isEqualTo(getSfpsAsset_fingerprintToError())
-                assertThat(iconContentDescriptionId).isEqualTo(R.string.biometric_dialog_try_again)
+                assertThat(iconContentDescriptionId).isEqualTo(bpTryAgainDescription)
                 assertThat(shouldAnimateIconView).isEqualTo(true)
             } else {
                 assertThat(iconAsset)
                     .isEqualTo(R.raw.fingerprint_dialogue_fingerprint_to_error_lottie)
-                assertThat(iconContentDescriptionId).isEqualTo(R.string.biometric_dialog_try_again)
+                assertThat(iconContentDescriptionId).isEqualTo(bpTryAgainDescription)
                 assertThat(shouldAnimateIconView).isEqualTo(true)
             }
 
@@ -425,14 +437,12 @@
 
             if (testCase.sensorType == FingerprintSensorProperties.TYPE_POWER_BUTTON) {
                 assertThat(iconAsset).isEqualTo(getSfpsAsset_errorToFingerprint())
-                assertThat(iconContentDescriptionId)
-                    .isEqualTo(R.string.security_settings_sfps_enroll_find_sensor_message)
+                assertThat(iconContentDescriptionId).isEqualTo(sfpsFindSensorDescription)
                 assertThat(shouldAnimateIconView).isEqualTo(true)
             } else {
                 assertThat(iconAsset)
                     .isEqualTo(R.raw.fingerprint_dialogue_error_to_fingerprint_lottie)
-                assertThat(iconContentDescriptionId)
-                    .isEqualTo(R.string.fingerprint_dialog_touch_sensor)
+                assertThat(iconContentDescriptionId).isEqualTo(udfpsIconDescription)
                 assertThat(shouldAnimateIconView).isEqualTo(true)
             }
         }
@@ -472,13 +482,12 @@
                     // Covers (1) fingerprint-only (2) co-ex, authenticated by fingerprint
                     if (testCase.authenticatedByFingerprint) {
                         assertThat(iconAsset).isEqualTo(R.raw.biometricprompt_sfps_error_to_success)
-                        assertThat(iconContentDescriptionId)
-                            .isEqualTo(R.string.security_settings_sfps_enroll_find_sensor_message)
+                        assertThat(iconContentDescriptionId).isEqualTo(sfpsFindSensorDescription)
                         assertThat(shouldAnimateIconView).isEqualTo(true)
                     } else { // Covers co-ex, authenticated by face
                         assertThat(iconAsset).isEqualTo(R.raw.biometricprompt_sfps_error_to_unlock)
                         assertThat(iconContentDescriptionId)
-                            .isEqualTo(R.string.fingerprint_dialog_authenticated_confirmation)
+                            .isEqualTo(fingerprintIconAuthenticatedDescription)
                         assertThat(shouldAnimateIconView).isEqualTo(true)
 
                         // Confirm authentication
@@ -486,8 +495,7 @@
 
                         assertThat(iconAsset)
                             .isEqualTo(R.raw.biometricprompt_sfps_unlock_to_success)
-                        assertThat(iconContentDescriptionId)
-                            .isEqualTo(R.string.fingerprint_dialog_touch_sensor)
+                        assertThat(iconContentDescriptionId).isEqualTo(udfpsIconDescription)
                         assertThat(shouldAnimateIconView).isEqualTo(true)
                     }
                 } else { // Non-SFPS (UDFPS / rear-FPS) test cases
@@ -495,14 +503,12 @@
                     if (testCase.authenticatedByFingerprint) {
                         assertThat(iconAsset)
                             .isEqualTo(R.raw.fingerprint_dialogue_error_to_success_lottie)
-                        assertThat(iconContentDescriptionId)
-                            .isEqualTo(R.string.fingerprint_dialog_touch_sensor)
+                        assertThat(iconContentDescriptionId).isEqualTo(udfpsIconDescription)
                         assertThat(shouldAnimateIconView).isEqualTo(true)
                     } else { //  co-ex, authenticated by face
                         assertThat(iconAsset)
                             .isEqualTo(R.raw.fingerprint_dialogue_error_to_unlock_lottie)
-                        assertThat(iconContentDescriptionId)
-                            .isEqualTo(R.string.biometric_dialog_confirm)
+                        assertThat(iconContentDescriptionId).isEqualTo(bpConfirmDescription)
                         assertThat(shouldAnimateIconView).isEqualTo(true)
 
                         // Confirm authentication
@@ -512,8 +518,7 @@
                             .isEqualTo(
                                 R.raw.fingerprint_dialogue_unlocked_to_checkmark_success_lottie
                             )
-                        assertThat(iconContentDescriptionId)
-                            .isEqualTo(R.string.fingerprint_dialog_touch_sensor)
+                        assertThat(iconContentDescriptionId).isEqualTo(udfpsIconDescription)
                         assertThat(shouldAnimateIconView).isEqualTo(true)
                     }
                 }
@@ -543,22 +548,19 @@
                     // Fingerprint icon asset assertions
                     if (testCase.sensorType == FingerprintSensorProperties.TYPE_POWER_BUTTON) {
                         assertThat(iconAsset).isEqualTo(getSfpsAsset_fingerprintToSuccess())
-                        assertThat(iconContentDescriptionId)
-                            .isEqualTo(R.string.security_settings_sfps_enroll_find_sensor_message)
+                        assertThat(iconContentDescriptionId).isEqualTo(sfpsFindSensorDescription)
                         assertThat(shouldAnimateIconView).isEqualTo(true)
                     } else {
                         assertThat(iconAsset)
                             .isEqualTo(R.raw.fingerprint_dialogue_fingerprint_to_success_lottie)
-                        assertThat(iconContentDescriptionId)
-                            .isEqualTo(R.string.fingerprint_dialog_touch_sensor)
+                        assertThat(iconContentDescriptionId).isEqualTo(udfpsIconDescription)
                         assertThat(shouldAnimateIconView).isEqualTo(true)
                     }
                 } else if (testCase.isFaceOnly || testCase.isCoex) {
                     // Face icon asset assertions
                     // If co-ex, use implicit flow (explicit flow always requires confirmation)
                     assertThat(iconAsset).isEqualTo(R.raw.face_dialog_dark_to_checkmark)
-                    assertThat(iconContentDescriptionId)
-                        .isEqualTo(R.string.biometric_dialog_face_icon_description_authenticated)
+                    assertThat(iconContentDescriptionId).isEqualTo(faceIconAuthedDescription)
                     assertThat(shouldAnimateIconView).isEqualTo(true)
                     assertThat(message).isEqualTo(PromptMessage.Empty)
                 }
@@ -586,20 +588,18 @@
 
                 if (testCase.isFaceOnly) {
                     assertThat(iconAsset).isEqualTo(R.raw.face_dialog_wink_from_dark)
-                    assertThat(iconContentDescriptionId)
-                        .isEqualTo(R.string.biometric_dialog_face_icon_description_authenticated)
+                    assertThat(iconContentDescriptionId).isEqualTo(faceIconAuthedDescription)
                     assertThat(shouldAnimateIconView).isEqualTo(true)
                 } else if (testCase.isCoex) { // explicit flow, confirmation requested
                     if (testCase.sensorType == FingerprintSensorProperties.TYPE_POWER_BUTTON) {
                         assertThat(iconAsset).isEqualTo(getSfpsAsset_fingerprintToUnlock())
                         assertThat(iconContentDescriptionId)
-                            .isEqualTo(R.string.fingerprint_dialog_authenticated_confirmation)
+                            .isEqualTo(fingerprintIconAuthenticatedDescription)
                         assertThat(shouldAnimateIconView).isEqualTo(true)
                     } else {
                         assertThat(iconAsset)
                             .isEqualTo(R.raw.fingerprint_dialogue_fingerprint_to_unlock_lottie)
-                        assertThat(iconContentDescriptionId)
-                            .isEqualTo(R.string.biometric_dialog_confirm)
+                        assertThat(iconContentDescriptionId).isEqualTo(bpConfirmDescription)
                         assertThat(shouldAnimateIconView).isEqualTo(true)
                     }
                 }
@@ -628,8 +628,7 @@
 
                 if (testCase.isFaceOnly) {
                     assertThat(iconAsset).isEqualTo(R.raw.face_dialog_dark_to_checkmark)
-                    assertThat(iconContentDescriptionId)
-                        .isEqualTo(R.string.biometric_dialog_face_icon_description_confirmed)
+                    assertThat(iconContentDescriptionId).isEqualTo(faceIconConfirmedDescription)
                     assertThat(shouldAnimateIconView).isEqualTo(true)
                 }
 
@@ -644,8 +643,7 @@
                             .isEqualTo(
                                 R.raw.fingerprint_dialogue_unlocked_to_checkmark_success_lottie
                             )
-                        assertThat(iconContentDescriptionId)
-                            .isEqualTo(R.string.fingerprint_dialog_touch_sensor)
+                        assertThat(iconContentDescriptionId).isEqualTo(udfpsIconDescription)
                         assertThat(shouldAnimateIconView).isEqualTo(true)
                     }
                 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/common/ui/data/repository/ConfigurationRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/common/ui/data/repository/ConfigurationRepositoryImplTest.kt
index a308c8e..3f4d3f8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/common/ui/data/repository/ConfigurationRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/common/ui/data/repository/ConfigurationRepositoryImplTest.kt
@@ -98,6 +98,21 @@
         }
 
     @Test
+    fun onMovedToDisplays_updatesOnMovedToDisplay() =
+        testScope.runTest {
+            val lastOnMovedToDisplay by collectLastValue(underTest.onMovedToDisplay)
+            assertThat(lastOnMovedToDisplay).isNull()
+
+            val configurationCallback = withArgCaptor {
+                verify(configurationController).addCallback(capture())
+            }
+
+            configurationCallback.onMovedToDisplay(1, Configuration())
+            runCurrent()
+            assertThat(lastOnMovedToDisplay).isEqualTo(1)
+        }
+
+    @Test
     fun onAnyConfigurationChange_updatesOnConfigChanged() =
         testScope.runTest {
             val lastAnyConfigurationChange by collectLastValue(underTest.onAnyConfigurationChange)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt
index e5f0d7c..68f4acd 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt
@@ -42,6 +42,7 @@
 import com.android.systemui.flags.fakeFeatureFlagsClassic
 import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
 import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
 import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
 import com.android.systemui.keyguard.shared.model.KeyguardState
@@ -494,6 +495,7 @@
 
                 // Start dreaming.
                 updateDreaming(true)
+                advanceTimeBy(KeyguardInteractor.IS_ABLE_TO_DREAM_DELAY_MS)
 
                 // Hub times out immediately.
                 assertThat(scene).isEqualTo(CommunalScenes.Blank)
@@ -650,6 +652,7 @@
 
                 // Start dreaming.
                 updateDreaming(true)
+                advanceTimeBy(KeyguardInteractor.IS_ABLE_TO_DREAM_DELAY_MS)
 
                 // Hub times out immediately.
                 assertThat(scene).isEqualTo(Scenes.Dream)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalAppWidgetViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalAppWidgetViewModelTest.kt
new file mode 100644
index 0000000..a8a3873
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalAppWidgetViewModelTest.kt
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.ui.viewmodel
+
+import android.appwidget.AppWidgetHost.AppWidgetHostListener
+import android.appwidget.AppWidgetHostView
+import android.platform.test.flag.junit.FlagsParameterization
+import android.util.SizeF
+import android.widget.RemoteViews
+import androidx.test.filters.SmallTest
+import com.android.systemui.Flags.FLAG_SECONDARY_USER_WIDGET_HOST
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.communal.shared.model.fakeGlanceableHubMultiUserHelper
+import com.android.systemui.communal.widgets.AppWidgetHostListenerDelegate
+import com.android.systemui.communal.widgets.CommunalAppWidgetHost
+import com.android.systemui.communal.widgets.GlanceableHubWidgetManager
+import com.android.systemui.concurrency.fakeExecutor
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.backgroundCoroutineContext
+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.testKosmos
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.doAnswer
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.verify
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
+
+@SmallTest
+@RunWith(ParameterizedAndroidJunit4::class)
+class CommunalAppWidgetViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
+    val kosmos = testKosmos()
+
+    init {
+        mSetFlagsRule.setFlagsParameterization(flags)
+    }
+
+    private val Kosmos.listenerDelegateFactory by
+        Kosmos.Fixture {
+            AppWidgetHostListenerDelegate.Factory { listener ->
+                AppWidgetHostListenerDelegate(fakeExecutor, listener)
+            }
+        }
+
+    private val Kosmos.appWidgetHost by
+        Kosmos.Fixture {
+            mock<CommunalAppWidgetHost> {
+                on { setListener(any(), any()) } doAnswer
+                    { invocation ->
+                        val callback = invocation.arguments[1] as AppWidgetHostListener
+                        callback.updateAppWidget(mock<RemoteViews>())
+                    }
+            }
+        }
+
+    private val Kosmos.glanceableHubWidgetManager by
+        Kosmos.Fixture {
+            mock<GlanceableHubWidgetManager> {
+                on { setAppWidgetHostListener(any(), any()) } doAnswer
+                    { invocation ->
+                        val callback = invocation.arguments[1] as AppWidgetHostListener
+                        callback.updateAppWidget(mock<RemoteViews>())
+                    }
+            }
+        }
+
+    private val Kosmos.underTest by
+        Kosmos.Fixture {
+            CommunalAppWidgetViewModel(
+                    backgroundCoroutineContext,
+                    { appWidgetHost },
+                    listenerDelegateFactory,
+                    { glanceableHubWidgetManager },
+                    fakeGlanceableHubMultiUserHelper,
+                )
+                .apply { activateIn(testScope) }
+        }
+
+    @Test
+    fun setListener() =
+        kosmos.runTest {
+            val listener = mock<AppWidgetHostListener>()
+
+            underTest.setListener(123, listener)
+            runAll()
+
+            verify(listener).updateAppWidget(any())
+        }
+
+    @Test
+    fun setListener_HSUM() =
+        kosmos.runTest {
+            fakeGlanceableHubMultiUserHelper.setIsInHeadlessSystemUser(true)
+            val listener = mock<AppWidgetHostListener>()
+
+            underTest.setListener(123, listener)
+            runAll()
+
+            verify(listener).updateAppWidget(any())
+        }
+
+    @Test
+    fun updateSize() =
+        kosmos.runTest {
+            val view = mock<AppWidgetHostView>()
+            val size = SizeF(/* width= */ 100f, /* height= */ 200f)
+
+            underTest.updateSize(size, view)
+            runAll()
+
+            verify(view)
+                .updateAppWidgetSize(
+                    /* newOptions = */ any(),
+                    /* minWidth = */ eq(100),
+                    /* minHeight = */ eq(200),
+                    /* maxWidth = */ eq(100),
+                    /* maxHeight = */ eq(200),
+                    /* ignorePadding = */ eq(true),
+                )
+        }
+
+    private fun Kosmos.runAll() {
+        runCurrent()
+        fakeExecutor.runAllReady()
+    }
+
+    private companion object {
+        @JvmStatic
+        @Parameters(name = "{0}")
+        fun getParams(): List<FlagsParameterization> {
+            return FlagsParameterization.allCombinationsOf(FLAG_SECONDARY_USER_WIDGET_HOST)
+        }
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt
index 5bbd3ff..18cc8bf 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt
@@ -172,16 +172,16 @@
         }
 
     @Test
-    fun selectedKey_onReorderWidgets_isCleared() =
+    fun selectedKey_onReorderWidgets_isSet() =
         testScope.runTest {
             val selectedKey by collectLastValue(underTest.selectedKey)
 
-            val key = CommunalContentModel.KEY.widget(123)
-            underTest.setSelectedKey(key)
-            assertThat(selectedKey).isEqualTo(key)
-
-            underTest.onReorderWidgetStart()
+            underTest.setSelectedKey(null)
             assertThat(selectedKey).isNull()
+
+            val key = CommunalContentModel.KEY.widget(123)
+            underTest.onReorderWidgetStart(key)
+            assertThat(selectedKey).isEqualTo(key)
         }
 
     @Test
@@ -234,7 +234,7 @@
 
     @Test
     fun reorderWidget_uiEventLogging_start() {
-        underTest.onReorderWidgetStart()
+        underTest.onReorderWidgetStart(CommunalContentModel.KEY.widget(123))
         verify(uiEventLogger).log(CommunalUiEvent.COMMUNAL_HUB_REORDER_WIDGET_START)
     }
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
index 763ea39..d70af28 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
@@ -19,6 +19,7 @@
 import android.content.ComponentName
 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.provider.Settings
 import android.widget.RemoteViews
@@ -26,6 +27,7 @@
 import com.android.compose.animation.scene.ObservableTransitionState
 import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
 import com.android.systemui.Flags.FLAG_COMMUNAL_RESPONSIVE_GRID
+import com.android.systemui.Flags.FLAG_GLANCEABLE_HUB_DIRECT_EDIT_MODE
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.communal.data.model.CommunalSmartspaceTimer
 import com.android.systemui.communal.data.repository.FakeCommunalMediaRepository
@@ -101,6 +103,7 @@
 import org.mockito.Mockito
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.any
 import org.mockito.kotlin.atLeastOnce
 import org.mockito.kotlin.eq
 import org.mockito.kotlin.mock
@@ -169,7 +172,6 @@
             kosmos.testDispatcher,
             testScope,
             kosmos.testScope.backgroundScope,
-            context.resources,
             kosmos.keyguardTransitionInteractor,
             kosmos.keyguardInteractor,
             mock<KeyguardIndicationController>(),
@@ -442,6 +444,7 @@
         }
 
     @Test
+    @DisableFlags(FLAG_GLANCEABLE_HUB_DIRECT_EDIT_MODE)
     fun customizeWidgetButton_showsThenHidesAfterTimeout() =
         testScope.runTest {
             tutorialRepository.setTutorialSettingState(Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED)
@@ -455,6 +458,7 @@
         }
 
     @Test
+    @DisableFlags(FLAG_GLANCEABLE_HUB_DIRECT_EDIT_MODE)
     fun customizeWidgetButton_onDismiss_hidesImmediately() =
         testScope.runTest {
             tutorialRepository.setTutorialSettingState(Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED)
@@ -468,6 +472,14 @@
         }
 
     @Test
+    @EnableFlags(FLAG_GLANCEABLE_HUB_DIRECT_EDIT_MODE)
+    fun longClickDirectlyStartsEditMode() =
+        testScope.runTest {
+            underTest.onLongClick()
+            verify(communalInteractor).showWidgetEditor(any())
+        }
+
+    @Test
     fun canChangeScene_shadeNotExpanded() =
         testScope.runTest {
             // On keyguard without any shade expansion.
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/complication/ComplicationHostViewControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/complication/ComplicationHostViewControllerTest.java
index dd3f991..2ae611d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/complication/ComplicationHostViewControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/complication/ComplicationHostViewControllerTest.java
@@ -133,6 +133,7 @@
      */
     @Test
     public void testViewModelObservation() {
+        mController.onViewAttached();
         final Observer<Collection<ComplicationViewModel>> observer =
                 captureComplicationViewModelsObserver();
 
@@ -152,6 +153,7 @@
 
     @Test
     public void testMalformedComplicationAddition() {
+        mController.onViewAttached();
         final Observer<Collection<ComplicationViewModel>> observer =
                 captureComplicationViewModelsObserver();
 
@@ -167,6 +169,7 @@
 
     @Test
     public void testNewComplicationsBeforeEntryAnimationsFinishSetToInvisible() {
+        mController.onViewAttached();
         final Observer<Collection<ComplicationViewModel>> observer =
                 captureComplicationViewModelsObserver();
 
@@ -181,6 +184,7 @@
 
     @Test
     public void testNewComplicationsAfterEntryAnimationsFinishNotSetToInvisible() {
+        mController.onViewAttached();
         final Observer<Collection<ComplicationViewModel>> observer =
                 captureComplicationViewModelsObserver();
 
@@ -198,6 +202,7 @@
 
     @Test
     public void testAnimationsDisabled_ComplicationsNeverSetToInvisible() {
+        mController.onViewAttached();
         //Disable animations
         mController.mIsAnimationEnabled = false;
 
@@ -213,6 +218,16 @@
         verify(mComplicationView, never()).setVisibility(View.INVISIBLE);
     }
 
+    @Test
+    public void testLifecycleObserve_activeOnlyDuringAttachedState() {
+        verify(mComplicationViewModelLiveData, never()).observe(any(), any());
+        mController.onViewAttached();
+        final Observer<Collection<ComplicationViewModel>> observer =
+                captureComplicationViewModelsObserver();
+        mController.onViewDetached();
+        verify(mComplicationViewModelLiveData).removeObserver(eq(observer));
+    }
+
     private Observer<Collection<ComplicationViewModel>> captureComplicationViewModelsObserver() {
         verify(mComplicationViewModelLiveData).observe(eq(mLifecycleOwner),
                 mObserverCaptor.capture());
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/doze/DozeScreenStateTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/doze/DozeScreenStateTest.java
index bbd78b3..4f2c1ed 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/doze/DozeScreenStateTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/doze/DozeScreenStateTest.java
@@ -50,6 +50,7 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.biometrics.AuthController;
 import com.android.systemui.biometrics.UdfpsController;
+import com.android.systemui.keyguard.domain.interactor.DozeInteractor;
 import com.android.systemui.statusbar.phone.DozeParameters;
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
 import com.android.systemui.util.wakelock.WakeLockFake;
@@ -87,6 +88,8 @@
     @Mock
     private DozeScreenBrightness mDozeScreenBrightness;
     @Mock
+    private DozeInteractor mDozeInteractor;
+    @Mock
     private SelectedUserInteractor mSelectedUserInteractor;
 
     @Before
@@ -103,7 +106,7 @@
         mWakeLock = new WakeLockFake();
         mScreen = new DozeScreenState(mServiceFake, mHandlerFake, mDozeHost, mDozeParameters,
                 mWakeLock, mAuthController, mUdfpsControllerProvider, mDozeLog,
-                mDozeScreenBrightness, mSelectedUserInteractor);
+                mDozeScreenBrightness, mDozeInteractor, mSelectedUserInteractor);
     }
 
     @Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/AppLaunchDataRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/AppLaunchDataRepositoryTest.kt
new file mode 100644
index 0000000..477e31e
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/AppLaunchDataRepositoryTest.kt
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyboard.shortcut.data.repository
+
+import android.hardware.input.AppLaunchData
+import android.hardware.input.AppLaunchData.RoleData
+import android.hardware.input.InputGestureData
+import android.hardware.input.InputGestureData.createKeyTrigger
+import android.hardware.input.fakeInputManager
+import android.view.KeyEvent.KEYCODE_A
+import android.view.KeyEvent.META_ALT_ON
+import android.view.KeyEvent.META_CTRL_ON
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.keyboard.shortcut.appLaunchDataRepository
+import com.android.systemui.keyboard.shortcut.shared.model.shortcutCommand
+import com.android.systemui.keyboard.shortcut.shortcutHelperTestHelper
+import com.android.systemui.kosmos.runTest
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.whenever
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class AppLaunchDataRepositoryTest : SysuiTestCase() {
+    private val kosmos = testKosmos().useUnconfinedTestDispatcher()
+    private val inputManager = kosmos.fakeInputManager.inputManager
+    private val testHelper = kosmos.shortcutHelperTestHelper
+    private val repo = kosmos.appLaunchDataRepository
+
+    @Test
+    fun appLaunchData_returnsDataRetrievedFromApiBasedOnShortcutCommand() =
+        kosmos.runTest {
+            val inputGesture = simpleInputGestureDataForAppLaunchShortcut()
+            setApiAppLaunchBookmarks(listOf(inputGesture))
+
+            testHelper.toggle(TEST_DEVICE_ID)
+
+            val appLaunchData =
+                repo.getAppLaunchDataForShortcutWithCommand(
+                    shortcutCommand =
+                        shortcutCommand {
+                            key("Ctrl")
+                            key("Alt")
+                            key("A")
+                        }
+                )
+
+            assertThat(appLaunchData).isEqualTo(inputGesture.action.appLaunchData())
+        }
+
+    @Test
+    fun appLaunchData_returnsSameDataForAnyOrderOfShortcutCommandKeys() =
+        kosmos.runTest {
+            val inputGesture = simpleInputGestureDataForAppLaunchShortcut()
+            setApiAppLaunchBookmarks(listOf(inputGesture))
+
+            testHelper.toggle(TEST_DEVICE_ID)
+
+            val shortcutCommandCtrlAltA = shortcutCommand {
+                key("Ctrl")
+                key("Alt")
+                key("A")
+            }
+
+            val shortcutCommandCtrlAAlt = shortcutCommand {
+                key("Ctrl")
+                key("A")
+                key("Alt")
+            }
+
+            val shortcutCommandAltCtrlA = shortcutCommand {
+                key("Alt")
+                key("Ctrl")
+                key("A")
+            }
+
+            assertThat(repo.getAppLaunchDataForShortcutWithCommand(shortcutCommandCtrlAltA))
+                .isEqualTo(inputGesture.action.appLaunchData())
+
+            assertThat(repo.getAppLaunchDataForShortcutWithCommand(shortcutCommandCtrlAAlt))
+                .isEqualTo(inputGesture.action.appLaunchData())
+
+            assertThat(repo.getAppLaunchDataForShortcutWithCommand(shortcutCommandAltCtrlA))
+                .isEqualTo(inputGesture.action.appLaunchData())
+        }
+
+    private fun setApiAppLaunchBookmarks(appLaunchBookmarks: List<InputGestureData>) {
+        whenever(inputManager.appLaunchBookmarks).thenReturn(appLaunchBookmarks)
+    }
+
+    private fun simpleInputGestureDataForAppLaunchShortcut(
+        keyCode: Int = KEYCODE_A,
+        modifiers: Int = META_CTRL_ON or META_ALT_ON,
+        appLaunchData: AppLaunchData = RoleData(TEST_ROLE),
+    ): InputGestureData {
+        return InputGestureData.Builder()
+            .setTrigger(createKeyTrigger(keyCode, modifiers))
+            .setAppLaunchData(appLaunchData)
+            .build()
+    }
+
+    private companion object {
+        private const val TEST_ROLE = "Test role"
+        private const val TEST_DEVICE_ID = 123
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepositoryTest.kt
index d12c045..4cfb26e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepositoryTest.kt
@@ -18,14 +18,21 @@
 
 import android.content.Context
 import android.content.Context.INPUT_SERVICE
+import android.hardware.input.AppLaunchData
+import android.hardware.input.AppLaunchData.RoleData
 import android.hardware.input.InputGestureData
+import android.hardware.input.InputGestureData.createKeyTrigger
 import android.hardware.input.InputManager.CUSTOM_INPUT_GESTURE_RESULT_ERROR_DOES_NOT_EXIST
 import android.hardware.input.InputManager.CUSTOM_INPUT_GESTURE_RESULT_SUCCESS
+import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION
 import android.hardware.input.fakeInputManager
 import android.platform.test.annotations.DisableFlags
 import android.platform.test.annotations.EnableFlags
+import android.view.KeyEvent.KEYCODE_A
 import android.view.KeyEvent.KEYCODE_SLASH
+import android.view.KeyEvent.META_ALT_ON
 import android.view.KeyEvent.META_CAPS_LOCK_ON
+import android.view.KeyEvent.META_CTRL_ON
 import android.view.KeyEvent.META_META_ON
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
@@ -44,14 +51,15 @@
 import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.customizableInputGestureWithUnknownKeyGestureType
 import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.expectedShortcutCategoriesWithSimpleShortcutCombination
 import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.goHomeInputGestureData
+import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.launchCalendarShortcutAddRequest
 import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.standardKeyCombination
 import com.android.systemui.keyboard.shortcut.shared.model.KeyCombination
 import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCustomizationRequestInfo
-import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCustomizationRequestInfo.Add
-import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCustomizationRequestInfo.Delete
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCustomizationRequestInfo.SingleShortcutCustomization
 import com.android.systemui.keyboard.shortcut.shared.model.ShortcutKey
 import com.android.systemui.keyboard.shortcut.shortcutHelperTestHelper
 import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
 import com.android.systemui.res.R
 import com.android.systemui.settings.FakeUserTracker
 import com.android.systemui.settings.userTracker
@@ -72,7 +80,7 @@
 
     private val mockUserContext: Context = mock()
     private val kosmos =
-        testKosmos().also {
+        testKosmos().useUnconfinedTestDispatcher().also {
             it.userTracker = FakeUserTracker(onCreateCurrentUserContext = { mockUserContext })
         }
 
@@ -242,6 +250,32 @@
     }
 
     @Test
+    fun buildInputGestureDataForAppLaunchShortcut_keyGestureTypeIsTypeLaunchApp() =
+        testScope.runTest {
+            setApiAppLaunchBookmarks(listOf(simpleInputGestureDataForAppLaunchShortcut()))
+            helper.toggle(deviceId = 123)
+            repo.onCustomizationRequested(launchCalendarShortcutAddRequest)
+            repo.updateUserKeyCombination(standardKeyCombination)
+
+            val inputGestureData = repo.buildInputGestureDataForShortcutBeingCustomized()
+
+            assertThat(inputGestureData?.action?.keyGestureType())
+                .isEqualTo(KEY_GESTURE_TYPE_LAUNCH_APPLICATION)
+        }
+
+    @Test
+    fun buildInputGestureDataForAppLaunchShortcut_appLaunchDataIsAdded() =
+        testScope.runTest {
+            setApiAppLaunchBookmarks(listOf(simpleInputGestureDataForAppLaunchShortcut()))
+            helper.toggle(deviceId = 123)
+            repo.onCustomizationRequested(launchCalendarShortcutAddRequest)
+            repo.updateUserKeyCombination(standardKeyCombination)
+
+            val inputGestureData = repo.buildInputGestureDataForShortcutBeingCustomized()
+            assertThat(inputGestureData?.action?.appLaunchData()).isNotNull()
+        }
+
+    @Test
     @EnableFlags(FLAG_ENABLE_CUSTOMIZABLE_INPUT_GESTURES, FLAG_USE_KEY_GESTURE_EVENT_HANDLER)
     fun deleteShortcut_successfullyRetrievesGestureDataAndDeletesShortcut() {
         testScope.runTest {
@@ -304,17 +338,17 @@
 
     private suspend fun customizeShortcut(
         customizationRequest: ShortcutCustomizationRequestInfo,
-        keyCombination: KeyCombination? = null
-    ): ShortcutCustomizationRequestResult{
+        keyCombination: KeyCombination? = null,
+    ): ShortcutCustomizationRequestResult {
         repo.onCustomizationRequested(customizationRequest)
         repo.updateUserKeyCombination(keyCombination)
 
         return when (customizationRequest) {
-            is Add -> {
+            is SingleShortcutCustomization.Add -> {
                 repo.confirmAndSetShortcutCurrentlyBeingCustomized()
             }
 
-            is Delete -> {
+            is SingleShortcutCustomization.Delete -> {
                 repo.deleteShortcutCurrentlyBeingCustomized()
             }
 
@@ -352,4 +386,19 @@
             assertThat(categories).isEmpty()
         }
     }
+
+    private fun setApiAppLaunchBookmarks(appLaunchBookmarks: List<InputGestureData>) {
+        whenever(inputManager.appLaunchBookmarks).thenReturn(appLaunchBookmarks)
+    }
+
+    private fun simpleInputGestureDataForAppLaunchShortcut(
+        keyCode: Int = KEYCODE_A,
+        modifiers: Int = META_CTRL_ON or META_ALT_ON,
+        appLaunchData: AppLaunchData = RoleData("Test role"),
+    ): InputGestureData {
+        return InputGestureData.Builder()
+            .setTrigger(createKeyTrigger(keyCode, modifiers))
+            .setAppLaunchData(appLaunchData)
+            .build()
+    }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/InputGestureDataAdapterTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/InputGestureDataAdapterTest.kt
index f78c692..9641059 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/InputGestureDataAdapterTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/InputGestureDataAdapterTest.kt
@@ -28,6 +28,7 @@
 import android.hardware.input.AppLaunchData.RoleData
 import android.hardware.input.InputGestureData
 import android.hardware.input.InputGestureData.createKeyTrigger
+import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION
 import android.view.KeyEvent.KEYCODE_A
 import android.view.KeyEvent.META_ALT_ON
 import android.view.KeyEvent.META_CTRL_ON
@@ -55,14 +56,15 @@
 import org.mockito.kotlin.mock
 import org.mockito.kotlin.whenever
 
-
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class InputGestureDataAdapterTest : SysuiTestCase() {
 
-    private val kosmos = testKosmos().also { kosmos ->
-        kosmos.userTracker = FakeUserTracker(onCreateCurrentUserContext = { kosmos.mockedContext })
-    }
+    private val kosmos =
+        testKosmos().also { kosmos ->
+            kosmos.userTracker =
+                FakeUserTracker(onCreateCurrentUserContext = { kosmos.mockedContext })
+        }
     private val adapter = kosmos.inputGestureDataAdapter
     private val roleManager = kosmos.roleManager
     private val packageManager: PackageManager = kosmos.packageManager
@@ -139,24 +141,40 @@
             val inputGestureData = buildInputGestureDataForAppLaunchShortcut()
             val internalGroups = adapter.toInternalGroupSources(listOf(inputGestureData))
 
-            assertThat(internalGroups).containsExactly(
-                InternalGroupsSource(
-                    type = ShortcutCategoryType.AppCategories,
-                    groups = listOf(
-                        InternalKeyboardShortcutGroup(
-                            label = APPLICATION_SHORTCUT_GROUP_LABEL,
-                            items = listOf(
-                                InternalKeyboardShortcutInfo(
-                                    label = expectedShortcutLabelForFirstAppMatchingIntent,
-                                    keycode = KEYCODE_A,
-                                    modifiers = META_CTRL_ON or META_ALT_ON,
-                                    isCustomShortcut = true
+            assertThat(internalGroups)
+                .containsExactly(
+                    InternalGroupsSource(
+                        type = ShortcutCategoryType.AppCategories,
+                        groups =
+                            listOf(
+                                InternalKeyboardShortcutGroup(
+                                    label = APPLICATION_SHORTCUT_GROUP_LABEL,
+                                    items =
+                                        listOf(
+                                            InternalKeyboardShortcutInfo(
+                                                label =
+                                                    expectedShortcutLabelForFirstAppMatchingIntent,
+                                                keycode = KEYCODE_A,
+                                                modifiers = META_CTRL_ON or META_ALT_ON,
+                                                isCustomShortcut = true,
+                                            )
+                                        ),
                                 )
-                            )
-                        )
+                            ),
                     )
                 )
-            )
+        }
+
+    @Test
+    fun keyGestureType_returnsTypeLaunchApplicationForAppLaunchShortcutCategory() =
+        kosmos.runTest {
+            assertThat(
+                    adapter.getKeyGestureTypeForShortcut(
+                        shortcutLabel = "Test Shortcut label",
+                        shortcutCategoryType = ShortcutCategoryType.AppCategories,
+                    )
+                )
+                .isEqualTo(KEY_GESTURE_TYPE_LAUNCH_APPLICATION)
         }
 
     private fun setApiToRetrieveResolverActivity() {
@@ -169,11 +187,10 @@
             .thenReturn(fakeActivityInfo)
     }
 
-
     private fun buildInputGestureDataForAppLaunchShortcut(
         keyCode: Int = KEYCODE_A,
         modifiers: Int = META_CTRL_ON or META_ALT_ON,
-        appLaunchData: AppLaunchData = RoleData(TEST_ROLE)
+        appLaunchData: AppLaunchData = RoleData(TEST_ROLE),
     ): InputGestureData {
         return InputGestureData.Builder()
             .setTrigger(createKeyTrigger(keyCode, modifiers))
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperInputDeviceRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperInputDeviceRepositoryTest.kt
new file mode 100644
index 0000000..ded2d22
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperInputDeviceRepositoryTest.kt
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyboard.shortcut.data.repository
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.keyboard.shortcut.shortcutHelperInputDeviceRepository
+import com.android.systemui.keyboard.shortcut.shortcutHelperTestHelper
+import com.android.systemui.kosmos.collectLastValue
+import com.android.systemui.kosmos.runTest
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class ShortcutHelperInputDeviceRepositoryTest : SysuiTestCase() {
+    private val kosmos = testKosmos().useUnconfinedTestDispatcher()
+    private val testHelper = kosmos.shortcutHelperTestHelper
+    private val repo = kosmos.shortcutHelperInputDeviceRepository
+
+    @Test
+    fun activeInputDevice_nullByDefault() =
+        kosmos.runTest {
+            val activeInputDevice by collectLastValue(repo.activeInputDevice)
+
+            assertThat(activeInputDevice).isNull()
+        }
+
+    @Test
+    fun activeInputDevice_nonNullWhenHelperIsShown() =
+        kosmos.runTest {
+            val activeInputDevice by collectLastValue(repo.activeInputDevice)
+
+            testHelper.showFromActivity()
+
+            assertThat(activeInputDevice).isNotNull()
+        }
+
+    @Test
+    fun activeInputDevice_nullWhenHelperIsClosed() =
+        kosmos.runTest {
+            val activeInputDevice by collectLastValue(repo.activeInputDevice)
+
+            testHelper.showFromActivity()
+            testHelper.hideFromActivity()
+
+            assertThat(activeInputDevice).isNull()
+        }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/source/TestShortcuts.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/source/TestShortcuts.kt
index 7dc7016..7c88d76 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/source/TestShortcuts.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/source/TestShortcuts.kt
@@ -43,11 +43,12 @@
 import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.MultiTasking
 import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.System
 import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCommand
-import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCustomizationRequestInfo
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCustomizationRequestInfo.SingleShortcutCustomization
 import com.android.systemui.keyboard.shortcut.shared.model.ShortcutKey
 import com.android.systemui.keyboard.shortcut.shared.model.ShortcutSubCategory
 import com.android.systemui.keyboard.shortcut.shared.model.shortcut
 import com.android.systemui.keyboard.shortcut.shared.model.shortcutCategory
+import com.android.systemui.keyboard.shortcut.shared.model.shortcutCommand
 import com.android.systemui.res.R
 
 object TestShortcuts {
@@ -596,14 +597,27 @@
         )
 
     val allAppsShortcutAddRequest =
-        ShortcutCustomizationRequestInfo.Add(
+        SingleShortcutCustomization.Add(
             label = "Open apps list",
             categoryType = System,
             subCategoryLabel = "System controls",
         )
 
+    val launchCalendarShortcutAddRequest =
+        SingleShortcutCustomization.Add(
+            label = "Calendar",
+            categoryType = ShortcutCategoryType.AppCategories,
+            subCategoryLabel = "Applications",
+            shortcutCommand =
+                shortcutCommand {
+                    key("Ctrl")
+                    key("Alt")
+                    key("A")
+                },
+        )
+
     val allAppsShortcutDeleteRequest =
-        ShortcutCustomizationRequestInfo.Delete(
+        SingleShortcutCustomization.Delete(
             label = "Open apps list",
             categoryType = System,
             subCategoryLabel = "System controls",
@@ -698,7 +712,7 @@
         )
 
     val standardAddShortcutRequest =
-        ShortcutCustomizationRequestInfo.Add(
+        SingleShortcutCustomization.Add(
             label = "Standard shortcut",
             categoryType = System,
             subCategoryLabel = "Standard subcategory",
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
index 77f1979..6805a13 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
@@ -123,27 +123,6 @@
         }
 
     @Test
-    fun bottomAreaAlpha() =
-        testScope.runTest {
-            assertThat(underTest.bottomAreaAlpha.value).isEqualTo(1f)
-
-            underTest.setBottomAreaAlpha(0.1f)
-            assertThat(underTest.bottomAreaAlpha.value).isEqualTo(0.1f)
-
-            underTest.setBottomAreaAlpha(0.2f)
-            assertThat(underTest.bottomAreaAlpha.value).isEqualTo(0.2f)
-
-            underTest.setBottomAreaAlpha(0.3f)
-            assertThat(underTest.bottomAreaAlpha.value).isEqualTo(0.3f)
-
-            underTest.setBottomAreaAlpha(0.5f)
-            assertThat(underTest.bottomAreaAlpha.value).isEqualTo(0.5f)
-
-            underTest.setBottomAreaAlpha(1.0f)
-            assertThat(underTest.bottomAreaAlpha.value).isEqualTo(1f)
-        }
-
-    @Test
     fun panelAlpha() =
         testScope.runTest {
             assertThat(underTest.panelAlpha.value).isEqualTo(1f)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractorTest.kt
index ead151e..b069855 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractorTest.kt
@@ -106,7 +106,7 @@
             repository.setKeyguardEnabled(true)
             runCurrent()
 
-            assertEquals(listOf(false, true, false), canWake)
+            assertEquals(listOf(false, true), canWake)
         }
 
     @Test
@@ -374,6 +374,8 @@
                     // Should be canceled by the wakeup, but there would still be an
                     // alarm in flight that should be canceled.
                     false,
+                    // True once we're actually GONE.
+                    true,
                 ),
                 canWake,
             )
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt
index 2101987..9d5bf4d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt
@@ -55,7 +55,6 @@
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
-import org.junit.Ignore
 import org.junit.Test
 import org.junit.runner.RunWith
 
@@ -887,7 +886,6 @@
 
     @Test
     @EnableSceneContainer
-    @Ignore("b/378766637")
     fun lockscreenVisibilityWithScenes() =
         testScope.runTest {
             val isDeviceUnlocked by
@@ -896,6 +894,7 @@
                 )
             assertThat(isDeviceUnlocked).isFalse()
 
+            kosmos.setSceneTransition(Idle(Scenes.Lockscreen))
             val currentScene by collectLastValue(kosmos.sceneInteractor.currentScene)
             assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt
index df4d5ab..ea2b3cd 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt
@@ -206,6 +206,9 @@
     @Test
     @RequiresFlagsDisabled(Flags.FLAG_ENSURE_KEYGUARD_DOES_TRANSITION_STARTING)
     fun setSurfaceBehindVisibility_falseSetsLockscreenVisibility_without_keyguard_shell_transitions() {
+        // Show the surface behind, then hide it.
+        underTest.setLockscreenShown(true)
+        underTest.setSurfaceBehindVisibility(true)
         underTest.setSurfaceBehindVisibility(false)
         verify(activityTaskManagerService).setLockScreenShown(eq(true), any())
     }
@@ -213,6 +216,9 @@
     @Test
     @RequiresFlagsEnabled(Flags.FLAG_ENSURE_KEYGUARD_DOES_TRANSITION_STARTING)
     fun setSurfaceBehindVisibility_falseSetsLockscreenVisibility_with_keyguard_shell_transitions() {
+        // Show the surface behind, then hide it.
+        underTest.setLockscreenShown(true)
+        underTest.setSurfaceBehindVisibility(true)
         underTest.setSurfaceBehindVisibility(false)
         verify(keyguardTransitions).startKeyguardTransition(eq(true), any())
     }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSectionTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSectionTest.kt
index 10f7128..9ceabd7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSectionTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSectionTest.kt
@@ -56,21 +56,12 @@
 
     @Test
     fun addViewsConditionally() {
-        mSetFlagsRule.enableFlags(AConfigFlags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR)
         val constraintLayout = ConstraintLayout(context, null)
         underTest.addViews(constraintLayout)
         assertThat(constraintLayout.childCount).isGreaterThan(0)
     }
 
     @Test
-    fun addViewsConditionally_migrateFlagOff() {
-        mSetFlagsRule.disableFlags(AConfigFlags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR)
-        val constraintLayout = ConstraintLayout(context, null)
-        underTest.addViews(constraintLayout)
-        assertThat(constraintLayout.childCount).isEqualTo(0)
-    }
-
-    @Test
     fun applyConstraints() {
         val cs = ConstraintSet()
         underTest.applyConstraints(cs)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToPrimaryBouncerTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToPrimaryBouncerTransitionViewModelTest.kt
index 9fab603..70d7a5f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToPrimaryBouncerTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToPrimaryBouncerTransitionViewModelTest.kt
@@ -27,7 +27,9 @@
 import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.keyguard.shared.model.TransitionState.RUNNING
 import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition
 import com.android.systemui.kosmos.testScope
+import com.android.systemui.shade.shadeTestUtil
 import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -47,6 +49,8 @@
     private val keyguardTransitionRepository by lazy { kosmos.fakeKeyguardTransitionRepository }
     private val underTest by lazy { kosmos.alternateBouncerToPrimaryBouncerTransitionViewModel }
 
+    private val shadeTestUtil by lazy { kosmos.shadeTestUtil }
+
     @Test
     fun deviceEntryParentViewDisappear() =
         testScope.runTest {
@@ -67,13 +71,44 @@
             values.forEach { assertThat(it).isEqualTo(0f) }
         }
 
+    @Test
+    fun blurRadiusGoesToMaximumWhenShadeIsExpanded() =
+        testScope.runTest {
+            val values by collectValues(underTest.windowBlurRadius)
+            kosmos.bouncerWindowBlurTestUtil.shadeExpanded(true)
+
+            kosmos.bouncerWindowBlurTestUtil.assertTransitionToBlurRadius(
+                transitionProgress = listOf(0f, 0f, 0.1f, 0.2f, 0.3f, 1f),
+                startValue = PrimaryBouncerTransition.MAX_BACKGROUND_BLUR_RADIUS,
+                endValue = PrimaryBouncerTransition.MAX_BACKGROUND_BLUR_RADIUS,
+                checkInterpolatedValues = false,
+                transitionFactory = ::step,
+                actualValuesProvider = { values },
+            )
+        }
+
+    @Test
+    fun blurRadiusGoesFromMinToMaxWhenShadeIsNotExpanded() =
+        testScope.runTest {
+            val values by collectValues(underTest.windowBlurRadius)
+            kosmos.bouncerWindowBlurTestUtil.shadeExpanded(false)
+
+            kosmos.bouncerWindowBlurTestUtil.assertTransitionToBlurRadius(
+                transitionProgress = listOf(0f, 0f, 0.1f, 0.2f, 0.3f, 1f),
+                startValue = PrimaryBouncerTransition.MIN_BACKGROUND_BLUR_RADIUS,
+                endValue = PrimaryBouncerTransition.MAX_BACKGROUND_BLUR_RADIUS,
+                transitionFactory = ::step,
+                actualValuesProvider = { values },
+            )
+        }
+
     private fun step(value: Float, state: TransitionState = RUNNING): TransitionStep {
         return TransitionStep(
             from = KeyguardState.ALTERNATE_BOUNCER,
             to = KeyguardState.PRIMARY_BOUNCER,
             value = value,
             transitionState = state,
-            ownerName = "AlternateBouncerToPrimaryBouncerTransitionViewModelTest"
+            ownerName = "AlternateBouncerToPrimaryBouncerTransitionViewModelTest",
         )
     }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodToPrimaryBouncerTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodToPrimaryBouncerTransitionViewModelTest.kt
new file mode 100644
index 0000000..0f239e8
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodToPrimaryBouncerTransitionViewModelTest.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.keyguard.ui.viewmodel
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectValues
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@ExperimentalCoroutinesApi
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class AodToPrimaryBouncerTransitionViewModelTest : SysuiTestCase() {
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+    private val underTest by lazy { kosmos.aodToPrimaryBouncerTransitionViewModel }
+
+    @Test
+    fun aodToPrimaryBouncerChangesBlurToMax() =
+        testScope.runTest {
+            val values by collectValues(underTest.windowBlurRadius)
+
+            kosmos.bouncerWindowBlurTestUtil.assertTransitionToBlurRadius(
+                transitionProgress = listOf(0.0f, 0.0f, 0.3f, 0.4f, 0.5f, 1.0f),
+                startValue = PrimaryBouncerTransition.MAX_BACKGROUND_BLUR_RADIUS,
+                endValue = PrimaryBouncerTransition.MAX_BACKGROUND_BLUR_RADIUS,
+                transitionFactory = { value, state ->
+                    TransitionStep(
+                        from = KeyguardState.AOD,
+                        to = KeyguardState.PRIMARY_BOUNCER,
+                        value = value,
+                        transitionState = state,
+                        ownerName = "AodToPrimaryBouncerTransitionViewModelTest",
+                    )
+                },
+                actualValuesProvider = { values },
+                checkInterpolatedValues = false,
+            )
+        }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/BouncerWindowBlurTestUtilKosmos.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/BouncerWindowBlurTestUtilKosmos.kt
new file mode 100644
index 0000000..c3f0deb
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/BouncerWindowBlurTestUtilKosmos.kt
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import android.util.MathUtils
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
+import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.StatusBarState
+import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionState.RUNNING
+import com.android.systemui.keyguard.shared.model.TransitionState.STARTED
+import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.shade.ShadeTestUtil
+import com.android.systemui.shade.shadeTestUtil
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.TestScope
+
+val Kosmos.bouncerWindowBlurTestUtil by
+    Kosmos.Fixture {
+        BouncerWindowBlurTestUtil(
+            shadeTestUtil = shadeTestUtil,
+            fakeKeyguardTransitionRepository = fakeKeyguardTransitionRepository,
+            fakeKeyguardRepository = fakeKeyguardRepository,
+            testScope = testScope,
+        )
+    }
+
+class BouncerWindowBlurTestUtil(
+    private val shadeTestUtil: ShadeTestUtil,
+    private val fakeKeyguardTransitionRepository: FakeKeyguardTransitionRepository,
+    private val fakeKeyguardRepository: FakeKeyguardRepository,
+    private val testScope: TestScope,
+) {
+
+    suspend fun assertTransitionToBlurRadius(
+        transitionProgress: List<Float>,
+        startValue: Float,
+        endValue: Float,
+        actualValuesProvider: () -> List<Float>,
+        transitionFactory: (value: Float, state: TransitionState) -> TransitionStep,
+        checkInterpolatedValues: Boolean = true,
+    ) {
+        val transitionSteps =
+            listOf(
+                transitionFactory(transitionProgress.first(), STARTED),
+                *transitionProgress.drop(1).map { transitionFactory(it, RUNNING) }.toTypedArray(),
+            )
+        fakeKeyguardTransitionRepository.sendTransitionSteps(transitionSteps, testScope)
+
+        val interpolationFunction = { step: Float -> MathUtils.lerp(startValue, endValue, step) }
+
+        if (checkInterpolatedValues) {
+            assertThat(actualValuesProvider.invoke())
+                .containsExactly(*transitionProgress.map(interpolationFunction).toTypedArray())
+        } else {
+            assertThat(actualValuesProvider.invoke()).contains(endValue)
+        }
+    }
+
+    fun shadeExpanded(expanded: Boolean) {
+        if (expanded) {
+            shadeTestUtil.setQsExpansion(1f)
+        } else {
+            fakeKeyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
+            shadeTestUtil.setQsExpansion(0f)
+            shadeTestUtil.setLockscreenShadeExpansion(0f)
+        }
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DozingToPrimaryBouncerTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DozingToPrimaryBouncerTransitionViewModelTest.kt
index bf71bec..7a68d4e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DozingToPrimaryBouncerTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DozingToPrimaryBouncerTransitionViewModelTest.kt
@@ -26,6 +26,7 @@
 import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.keyguard.shared.model.TransitionState.RUNNING
 import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
@@ -70,13 +71,27 @@
             values.forEach { assertThat(it).isEqualTo(0f) }
         }
 
+    @Test
+    fun windowBlurRadiusGoesFromMinToMax() =
+        testScope.runTest {
+            val values by collectValues(underTest.windowBlurRadius)
+
+            kosmos.bouncerWindowBlurTestUtil.assertTransitionToBlurRadius(
+                transitionProgress = listOf(0.0f, 0.2f, 0.3f, 0.65f, 0.7f, 1.0f),
+                startValue = PrimaryBouncerTransition.MIN_BACKGROUND_BLUR_RADIUS,
+                endValue = PrimaryBouncerTransition.MAX_BACKGROUND_BLUR_RADIUS,
+                actualValuesProvider = { values },
+                transitionFactory = ::step,
+            )
+        }
+
     private fun step(value: Float, state: TransitionState = RUNNING): TransitionStep {
         return TransitionStep(
             from = KeyguardState.DOZING,
             to = KeyguardState.PRIMARY_BOUNCER,
             value = value,
             transitionState = state,
-            ownerName = "DozingToPrimaryBouncerTransitionViewModelTest"
+            ownerName = "DozingToPrimaryBouncerTransitionViewModelTest",
         )
     }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModelTest.kt
index 6f74ed3..242ee3a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModelTest.kt
@@ -20,7 +20,6 @@
 import android.platform.test.flag.junit.FlagsParameterization
 import androidx.test.filters.SmallTest
 import com.android.compose.animation.scene.ObservableTransitionState
-import com.android.systemui.Flags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR
 import com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.common.ui.domain.interactor.configurationInteractor
@@ -31,7 +30,6 @@
 import com.android.systemui.doze.util.BurnInHelperWrapper
 import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
 import com.android.systemui.keyguard.domain.interactor.BurnInInteractor
-import com.android.systemui.keyguard.domain.interactor.keyguardBottomAreaInteractor
 import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
 import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
 import com.android.systemui.keyguard.shared.model.BurnInModel
@@ -61,8 +59,6 @@
 class KeyguardIndicationAreaViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
     private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
-
-    private val bottomAreaInteractor = kosmos.keyguardBottomAreaInteractor
     private lateinit var underTest: KeyguardIndicationAreaViewModel
     private val keyguardRepository = kosmos.fakeKeyguardRepository
     private val communalSceneRepository = kosmos.fakeCommunalSceneRepository
@@ -87,12 +83,6 @@
 
     @Before
     fun setUp() {
-        val bottomAreaViewModel =
-            mock<KeyguardBottomAreaViewModel> {
-                on { startButton } doReturn startButtonFlow
-                on { endButton } doReturn endButtonFlow
-                on { alpha } doReturn alphaFlow
-            }
         val burnInInteractor =
             mock<BurnInInteractor> {
                 on { burnIn(anyInt(), anyInt()) } doReturn flowOf(BurnInModel())
@@ -109,8 +99,6 @@
         underTest =
             KeyguardIndicationAreaViewModel(
                 keyguardInteractor = kosmos.keyguardInteractor,
-                bottomAreaInteractor = bottomAreaInteractor,
-                keyguardBottomAreaViewModel = bottomAreaViewModel,
                 burnInHelperWrapper = burnInHelperWrapper,
                 burnInInteractor = burnInInteractor,
                 shortcutsCombinedViewModel = shortcutsCombinedViewModel,
@@ -123,23 +111,6 @@
     }
 
     @Test
-    fun alpha() =
-        testScope.runTest {
-            val alpha by collectLastValue(underTest.alpha)
-
-            assertThat(alpha).isEqualTo(1f)
-            alphaFlow.value = 0.1f
-            assertThat(alpha).isEqualTo(0.1f)
-            alphaFlow.value = 0.5f
-            assertThat(alpha).isEqualTo(0.5f)
-            alphaFlow.value = 0.2f
-            assertThat(alpha).isEqualTo(0.2f)
-            alphaFlow.value = 0f
-            assertThat(alpha).isEqualTo(0f)
-        }
-
-    @Test
-    @DisableFlags(FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR)
     fun isIndicationAreaPadded() =
         testScope.runTest {
             keyguardRepository.setKeyguardShowing(true)
@@ -157,23 +128,6 @@
         }
 
     @Test
-    @DisableFlags(FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT, FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR)
-    fun indicationAreaTranslationX() =
-        testScope.runTest {
-            val translationX by collectLastValue(underTest.indicationAreaTranslationX)
-
-            assertThat(translationX).isEqualTo(0f)
-            bottomAreaInteractor.setClockPosition(100, 100)
-            assertThat(translationX).isEqualTo(100f)
-            bottomAreaInteractor.setClockPosition(200, 100)
-            assertThat(translationX).isEqualTo(200f)
-            bottomAreaInteractor.setClockPosition(200, 200)
-            assertThat(translationX).isEqualTo(200f)
-            bottomAreaInteractor.setClockPosition(300, 100)
-            assertThat(translationX).isEqualTo(300f)
-        }
-
-    @Test
     @DisableFlags(FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
     fun indicationAreaTranslationY() =
         testScope.runTest {
@@ -236,7 +190,6 @@
         @Parameters(name = "{0}")
         fun getParams(): List<FlagsParameterization> {
             return FlagsParameterization.allCombinationsOf(
-                FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR,
                 FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT,
             )
         }
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 b5e670c..95ffc96 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
@@ -24,7 +24,6 @@
 import android.view.View
 import androidx.test.filters.SmallTest
 import com.android.compose.animation.scene.ObservableTransitionState
-import com.android.systemui.Flags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR
 import com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.communal.data.repository.communalSceneRepository
@@ -74,7 +73,7 @@
 
 @SmallTest
 @RunWith(ParameterizedAndroidJunit4::class)
-@EnableFlags(FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT, FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR)
+@EnableFlags(FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
 class KeyguardRootViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
     private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt
index 1c1fcc4..fba3997 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt
@@ -21,6 +21,7 @@
 import com.android.compose.animation.scene.ObservableTransitionState
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.coroutines.collectValues
 import com.android.systemui.flags.BrokenWithSceneContainer
 import com.android.systemui.flags.Flags
 import com.android.systemui.flags.andSceneContainer
@@ -31,6 +32,7 @@
 import com.android.systemui.keyguard.shared.model.StatusBarState
 import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.scene.data.repository.sceneContainerRepository
 import com.android.systemui.scene.shared.flag.SceneContainerFlag
@@ -128,7 +130,7 @@
                     emptyFlow(),
                     emptyFlow(),
                     false,
-                    emptyFlow()
+                    emptyFlow(),
                 )
             runCurrent()
             // fade out
@@ -150,6 +152,39 @@
             Truth.assertThat(actual).isEqualTo(0f)
         }
 
+    @Test
+    @BrokenWithSceneContainer(330311871)
+    fun blurRadiusIsMaxWhenShadeIsExpanded() =
+        testScope.runTest {
+            val values by collectValues(underTest.windowBlurRadius)
+            kosmos.bouncerWindowBlurTestUtil.shadeExpanded(true)
+
+            kosmos.bouncerWindowBlurTestUtil.assertTransitionToBlurRadius(
+                transitionProgress = listOf(0.0f, 0.2f, 0.3f, 0.65f, 0.7f, 1.0f),
+                startValue = PrimaryBouncerTransition.MAX_BACKGROUND_BLUR_RADIUS,
+                endValue = PrimaryBouncerTransition.MAX_BACKGROUND_BLUR_RADIUS,
+                actualValuesProvider = { values },
+                transitionFactory = ::step,
+                checkInterpolatedValues = false,
+            )
+        }
+
+    @Test
+    @BrokenWithSceneContainer(330311871)
+    fun blurRadiusGoesFromMinToMaxWhenShadeIsNotExpanded() =
+        testScope.runTest {
+            val values by collectValues(underTest.windowBlurRadius)
+            kosmos.bouncerWindowBlurTestUtil.shadeExpanded(false)
+
+            kosmos.bouncerWindowBlurTestUtil.assertTransitionToBlurRadius(
+                transitionProgress = listOf(0.0f, 0.2f, 0.3f, 0.65f, 0.7f, 1.0f),
+                startValue = PrimaryBouncerTransition.MIN_BACKGROUND_BLUR_RADIUS,
+                endValue = PrimaryBouncerTransition.MAX_BACKGROUND_BLUR_RADIUS,
+                actualValuesProvider = { values },
+                transitionFactory = ::step,
+            )
+        }
+
     private fun step(
         value: Float,
         state: TransitionState = TransitionState.RUNNING,
@@ -161,7 +196,7 @@
                 else KeyguardState.PRIMARY_BOUNCER,
             value = value,
             transitionState = state,
-            ownerName = "LockscreenToPrimaryBouncerTransitionViewModelTest"
+            ownerName = "LockscreenToPrimaryBouncerTransitionViewModelTest",
         )
     }
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModelTest.kt
index c55c27c..b406e6c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModelTest.kt
@@ -21,11 +21,13 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.biometrics.data.repository.fingerprintPropertyRepository
 import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.coroutines.collectValues
 import com.android.systemui.keyguard.data.repository.biometricSettingsRepository
 import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
@@ -138,16 +140,31 @@
             assertThat(deviceEntryParentViewAlpha).isNull()
         }
 
+    @Test
+    fun blurRadiusGoesToMinImmediately() =
+        testScope.runTest {
+            val values by collectValues(underTest.windowBlurRadius)
+
+            kosmos.bouncerWindowBlurTestUtil.assertTransitionToBlurRadius(
+                transitionProgress = listOf(0.0f, 0.2f, 0.3f, 0.65f, 0.7f, 1.0f),
+                startValue = PrimaryBouncerTransition.MIN_BACKGROUND_BLUR_RADIUS,
+                endValue = PrimaryBouncerTransition.MIN_BACKGROUND_BLUR_RADIUS,
+                actualValuesProvider = { values },
+                transitionFactory = ::step,
+                checkInterpolatedValues = false,
+            )
+        }
+
     private fun step(
         value: Float,
-        state: TransitionState = TransitionState.RUNNING
+        state: TransitionState = TransitionState.RUNNING,
     ): TransitionStep {
         return TransitionStep(
             from = KeyguardState.PRIMARY_BOUNCER,
             to = KeyguardState.AOD,
             value = value,
             transitionState = state,
-            ownerName = "PrimaryBouncerToAodTransitionViewModelTest"
+            ownerName = "PrimaryBouncerToAodTransitionViewModelTest",
         )
     }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToDozingTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToDozingTransitionViewModelTest.kt
index 28473b2..a8f0f2f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToDozingTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToDozingTransitionViewModelTest.kt
@@ -30,6 +30,7 @@
 import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.keyguard.shared.model.TransitionState.RUNNING
 import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
@@ -122,13 +123,28 @@
             values.forEach { assertThat(it).isEqualTo(0f) }
         }
 
+    @Test
+    fun blurRadiusGoesToMinImmediately() =
+        testScope.runTest {
+            val values by collectValues(underTest.windowBlurRadius)
+
+            kosmos.bouncerWindowBlurTestUtil.assertTransitionToBlurRadius(
+                transitionProgress = listOf(0.0f, 0.2f, 0.3f, 0.65f, 0.7f, 1.0f),
+                startValue = PrimaryBouncerTransition.MIN_BACKGROUND_BLUR_RADIUS,
+                endValue = PrimaryBouncerTransition.MIN_BACKGROUND_BLUR_RADIUS,
+                actualValuesProvider = { values },
+                transitionFactory = ::step,
+                checkInterpolatedValues = false,
+            )
+        }
+
     private fun step(value: Float, state: TransitionState = RUNNING): TransitionStep {
         return TransitionStep(
             from = KeyguardState.PRIMARY_BOUNCER,
             to = KeyguardState.DOZING,
             value = value,
             transitionState = state,
-            ownerName = "PrimaryBouncerToDozingTransitionViewModelTest"
+            ownerName = "PrimaryBouncerToDozingTransitionViewModelTest",
         )
     }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGlanceableHubTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGlanceableHubTransitionViewModelTest.kt
new file mode 100644
index 0000000..2c6e553
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGlanceableHubTransitionViewModelTest.kt
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+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.flags.DisableSceneContainer
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@ExperimentalCoroutinesApi
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class PrimaryBouncerToGlanceableHubTransitionViewModelTest : SysuiTestCase() {
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+    private val underTest by lazy { kosmos.primaryBouncerToGlanceableHubTransitionViewModel }
+
+    @Test
+    @DisableSceneContainer
+    fun blurBecomesMinValueImmediately() =
+        testScope.runTest {
+            val values by collectValues(underTest.windowBlurRadius)
+
+            kosmos.bouncerWindowBlurTestUtil.assertTransitionToBlurRadius(
+                transitionProgress = listOf(0.0f, 0.2f, 0.3f, 0.65f, 0.7f, 1.0f),
+                startValue = PrimaryBouncerTransition.MIN_BACKGROUND_BLUR_RADIUS,
+                endValue = PrimaryBouncerTransition.MIN_BACKGROUND_BLUR_RADIUS,
+                actualValuesProvider = { values },
+                transitionFactory = { step, transitionState ->
+                    TransitionStep(
+                        from = KeyguardState.PRIMARY_BOUNCER,
+                        to = KeyguardState.GLANCEABLE_HUB,
+                        value = step,
+                        transitionState = transitionState,
+                        ownerName = "PrimaryBouncerToGlanceableHubTransitionViewModelTest",
+                    )
+                },
+                checkInterpolatedValues = false,
+            )
+        }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModelTest.kt
index 5ec566b..9e5976f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModelTest.kt
@@ -21,11 +21,13 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.biometrics.data.repository.fingerprintPropertyRepository
 import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.coroutines.collectValues
 import com.android.systemui.keyguard.data.repository.biometricSettingsRepository
 import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.testKosmos
 import com.google.common.collect.Range
@@ -110,16 +112,47 @@
             assertThat(bgViewAlpha).isEqualTo(1f)
         }
 
+    @Test
+    fun blurRadiusGoesFromMaxToMinWhenShadeIsNotExpanded() =
+        testScope.runTest {
+            val values by collectValues(underTest.windowBlurRadius)
+            kosmos.bouncerWindowBlurTestUtil.shadeExpanded(false)
+
+            kosmos.bouncerWindowBlurTestUtil.assertTransitionToBlurRadius(
+                transitionProgress = listOf(0.0f, 0.2f, 0.3f, 0.65f, 0.7f, 1.0f),
+                startValue = PrimaryBouncerTransition.MAX_BACKGROUND_BLUR_RADIUS,
+                endValue = PrimaryBouncerTransition.MIN_BACKGROUND_BLUR_RADIUS,
+                actualValuesProvider = { values },
+                transitionFactory = ::step,
+            )
+        }
+
+    @Test
+    fun blurRadiusRemainsAtMaxWhenShadeIsExpanded() =
+        testScope.runTest {
+            val values by collectValues(underTest.windowBlurRadius)
+            kosmos.bouncerWindowBlurTestUtil.shadeExpanded(true)
+
+            kosmos.bouncerWindowBlurTestUtil.assertTransitionToBlurRadius(
+                transitionProgress = listOf(0.0f, 0.2f, 0.3f, 0.65f, 0.7f, 1.0f),
+                startValue = PrimaryBouncerTransition.MAX_BACKGROUND_BLUR_RADIUS,
+                endValue = PrimaryBouncerTransition.MAX_BACKGROUND_BLUR_RADIUS,
+                actualValuesProvider = { values },
+                transitionFactory = ::step,
+                checkInterpolatedValues = false,
+            )
+        }
+
     private fun step(
         value: Float,
-        state: TransitionState = TransitionState.RUNNING
+        state: TransitionState = TransitionState.RUNNING,
     ): TransitionStep {
         return TransitionStep(
             from = KeyguardState.PRIMARY_BOUNCER,
             to = KeyguardState.LOCKSCREEN,
             value = value,
             transitionState = state,
-            ownerName = "PrimaryBouncerToLockscreenTransitionViewModelTest"
+            ownerName = "PrimaryBouncerToLockscreenTransitionViewModelTest",
         )
     }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModelTest.kt
index 9edd62a..6a2aae1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModelTest.kt
@@ -16,21 +16,23 @@
 
 package com.android.systemui.media.controls.ui.viewmodel
 
-import android.R
 import android.content.packageManager
 import android.content.pm.ApplicationInfo
 import android.media.MediaMetadata
 import android.media.session.MediaSession
 import android.media.session.PlaybackState
+import androidx.constraintlayout.widget.ConstraintSet
 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.media.controls.domain.pipeline.mediaDataFilter
+import com.android.systemui.media.controls.shared.model.MediaButton
 import com.android.systemui.media.controls.shared.model.MediaData
 import com.android.systemui.media.controls.shared.model.MediaDeviceData
 import com.android.systemui.media.controls.util.mediaInstanceId
+import com.android.systemui.res.R
 import com.android.systemui.statusbar.notificationLockscreenUserManager
 import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
@@ -132,6 +134,31 @@
             assertThat(underTest.setPlayer(playerModel!!)).isTrue()
         }
 
+    @Test
+    fun reservedButtons_showScrubbingTimes() =
+        testScope.runTest {
+            val playerModel by collectLastValue(underTest.player)
+            val mediaData =
+                initMediaData(ARTIST, TITLE)
+                    .copy(semanticActions = MediaButton(reserveNext = true, reservePrev = true))
+
+            mediaDataFilter.onMediaDataLoaded(KEY, KEY, mediaData)
+
+            assertThat(playerModel?.actionButtons).isNotNull()
+            assertThat(playerModel!!.useSemanticActions).isTrue()
+            assertThat(playerModel!!.canShowTime).isTrue()
+
+            val buttons = playerModel!!.actionButtons
+
+            val prevButton = buttons.find { it.buttonId == R.id.actionPrev }!!
+            assertThat(prevButton.notVisibleValue).isEqualTo(ConstraintSet.GONE)
+            assertThat(prevButton.isVisibleWhenScrubbing).isEqualTo(false)
+
+            val nextButton = buttons.find { it.buttonId == R.id.actionNext }!!
+            assertThat(nextButton.notVisibleValue).isEqualTo(ConstraintSet.GONE)
+            assertThat(nextButton.isVisibleWhenScrubbing).isEqualTo(false)
+        }
+
     private fun initMediaData(artist: String, title: String): MediaData {
         val device = MediaDeviceData(true, null, DEVICE_NAME, null, showBroadcastButton = true)
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/mediarouter/data/repository/MediaRouterRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/mediarouter/data/repository/MediaRouterRepositoryTest.kt
index 6ec38ba..77be8c7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/mediarouter/data/repository/MediaRouterRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/mediarouter/data/repository/MediaRouterRepositoryTest.kt
@@ -16,9 +16,8 @@
 
 package com.android.systemui.mediarouter.data.repository
 
-import android.media.projection.StopReason
-import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.kosmos.Kosmos
@@ -102,7 +101,7 @@
                 origin = CastDevice.CastOrigin.MediaRouter,
             )
 
-        underTest.stopCasting(device, StopReason.STOP_UNKNOWN)
+        underTest.stopCasting(device)
 
         assertThat(castController.lastStoppedDevice).isEqualTo(device)
     }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/TaskbarDelegateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/TaskbarDelegateTest.kt
index d921dde..a36e0ea 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/TaskbarDelegateTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/TaskbarDelegateTest.kt
@@ -9,6 +9,7 @@
 import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.recents.OverviewProxyService
+import com.android.systemui.settings.DisplayTracker
 import com.android.systemui.shared.system.QuickStepContract
 import com.android.systemui.shared.system.TaskStackChangeListeners
 import com.android.systemui.statusbar.CommandQueue
@@ -59,6 +60,7 @@
     @Mock lateinit var mCurrentSysUiState: NavBarHelper.CurrentSysuiState
     @Mock lateinit var mStatusBarKeyguardViewManager: StatusBarKeyguardViewManager
     @Mock lateinit var mStatusBarStateController: StatusBarStateController
+    @Mock lateinit var mDisplayTracker: DisplayTracker
 
     @Before
     fun setup() {
@@ -87,6 +89,7 @@
             mOptionalPip,
             mBackAnimation,
             mTaskStackChangeListeners,
+            mDisplayTracker,
         )
     }
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/power/domain/interactor/PowerInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/power/domain/interactor/PowerInteractorTest.kt
index 53dec69..f725e06 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/power/domain/interactor/PowerInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/power/domain/interactor/PowerInteractorTest.kt
@@ -18,6 +18,7 @@
 package com.android.systemui.power.domain.interactor
 
 import android.os.PowerManager
+import android.view.Display
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
@@ -25,9 +26,12 @@
 import com.android.systemui.classifier.FalsingCollector
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.keyguard.domain.interactor.dozeInteractor
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.power.data.repository.FakePowerRepository
+import com.android.systemui.power.data.repository.fakePowerRepository
+import com.android.systemui.power.shared.model.DozeScreenStateModel
 import com.android.systemui.power.shared.model.WakeSleepReason
 import com.android.systemui.power.shared.model.WakefulnessState
 import com.android.systemui.statusbar.phone.ScreenOffAnimationController
@@ -51,9 +55,9 @@
     private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
     private val cameraGestureHelper = kosmos.cameraGestureHelper
+    private val repository: FakePowerRepository = kosmos.fakePowerRepository
 
     private lateinit var underTest: PowerInteractor
-    private lateinit var repository: FakePowerRepository
     private val keyguardRepository = FakeKeyguardRepository()
     @Mock private lateinit var falsingCollector: FalsingCollector
     @Mock private lateinit var screenOffAnimationController: ScreenOffAnimationController
@@ -63,7 +67,6 @@
     fun setUp() {
         MockitoAnnotations.initMocks(this)
 
-        repository = FakePowerRepository()
         underTest =
             PowerInteractor(
                 repository,
@@ -208,7 +211,7 @@
         whenever(cameraGestureHelper.canCameraGestureBeLaunched(any())).thenReturn(false)
         underTest.onStartedWakingUp(
             PowerManager.WAKE_REASON_POWER_BUTTON,
-            /*powerButtonLaunchGestureTriggeredDuringSleep= */ false
+            /*powerButtonLaunchGestureTriggeredDuringSleep= */ false,
         )
         underTest.onFinishedWakingUp()
         underTest.onCameraLaunchGestureDetected()
@@ -224,7 +227,7 @@
     fun onCameraLaunchGestureDetected_maintainsAllOtherState() {
         underTest.onStartedWakingUp(
             PowerManager.WAKE_REASON_POWER_BUTTON,
-            /*powerButtonLaunchGestureTriggeredDuringSleep= */ false
+            /*powerButtonLaunchGestureTriggeredDuringSleep= */ false,
         )
         underTest.onFinishedWakingUp()
         underTest.onCameraLaunchGestureDetected()
@@ -244,7 +247,7 @@
         underTest.onFinishedGoingToSleep(/* powerButtonLaunchGestureTriggeredDuringSleep= */ false)
         underTest.onStartedWakingUp(
             PowerManager.WAKE_REASON_POWER_BUTTON,
-            /*powerButtonLaunchGestureTriggeredDuringSleep= */ false
+            /*powerButtonLaunchGestureTriggeredDuringSleep= */ false,
         )
         underTest.onFinishedWakingUp()
 
@@ -262,7 +265,7 @@
         // This state should only be reset onStartedGoingToSleep.
         underTest.onStartedWakingUp(
             PowerManager.WAKE_REASON_POWER_BUTTON,
-            /*powerButtonLaunchGestureTriggeredDuringSleep= */ false
+            /*powerButtonLaunchGestureTriggeredDuringSleep= */ false,
         )
         underTest.onFinishedWakingUp()
 
@@ -272,4 +275,26 @@
             .isEqualTo(WakeSleepReason.POWER_BUTTON)
         assertTrue(repository.wakefulness.value.powerButtonLaunchGestureTriggered)
     }
+
+    @Test
+    fun dozeScreenState() =
+        testScope.runTest {
+            val dozeScreenState by collectLastValue(underTest.dozeScreenState)
+            assertThat(dozeScreenState).isEqualTo(DozeScreenStateModel.UNKNOWN)
+
+            kosmos.dozeInteractor.setDozeScreenState(Display.STATE_OFF)
+            assertThat(dozeScreenState).isEqualTo(DozeScreenStateModel.OFF)
+
+            kosmos.dozeInteractor.setDozeScreenState(Display.STATE_ON)
+            assertThat(dozeScreenState).isEqualTo(DozeScreenStateModel.ON)
+
+            kosmos.dozeInteractor.setDozeScreenState(Display.STATE_DOZE)
+            assertThat(dozeScreenState).isEqualTo(DozeScreenStateModel.DOZE)
+
+            kosmos.dozeInteractor.setDozeScreenState(Display.STATE_DOZE_SUSPEND)
+            assertThat(dozeScreenState).isEqualTo(DozeScreenStateModel.DOZE_SUSPEND)
+
+            kosmos.dozeInteractor.setDozeScreenState(Display.STATE_ON_SUSPEND)
+            assertThat(dozeScreenState).isEqualTo(DozeScreenStateModel.ON_SUSPEND)
+        }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/FgsManagerControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/FgsManagerControllerTest.java
index 0356422..004aeb0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/FgsManagerControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/FgsManagerControllerTest.java
@@ -122,7 +122,7 @@
         mSystemClock = new FakeSystemClock();
         mMainExecutor = new FakeExecutor(mSystemClock);
         mBackgroundExecutor = new FakeExecutor(mSystemClock);
-        when(mSystemUIDialogFactory.create()).thenReturn(mSystemUIDialog);
+        when(mSystemUIDialogFactory.create(eq(mContext))).thenReturn(mSystemUIDialog);
 
         mUserProfiles = new ArrayList<>();
         Mockito.doReturn(mUserProfiles).when(mUserTracker).getUserProfiles();
@@ -346,6 +346,7 @@
                 SystemUiDeviceConfigFlags.TASK_MANAGER_SHOW_USER_VISIBLE_JOBS,
                 "true", false);
         FgsManagerController fmc = new FgsManagerControllerImpl(
+                mContext,
                 mContext.getResources(),
                 mMainExecutor,
                 mBackgroundExecutor,
@@ -373,6 +374,7 @@
                 SystemUiDeviceConfigFlags.TASK_MANAGER_SHOW_USER_VISIBLE_JOBS,
                 "false", false);
         fmc = new FgsManagerControllerImpl(
+                mContext,
                 mContext.getResources(),
                 mMainExecutor,
                 mBackgroundExecutor,
@@ -485,6 +487,7 @@
                 ArgumentCaptor.forClass(BroadcastReceiver.class);
 
         FgsManagerController result = new FgsManagerControllerImpl(
+                mContext,
                 mContext.getResources(),
                 mMainExecutor,
                 mBackgroundExecutor,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractorTest.kt
index c5a2370..6546a50 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractorTest.kt
@@ -35,9 +35,7 @@
 import com.android.systemui.statusbar.policy.DeviceProvisionedController
 import com.android.systemui.truth.correspondence.FakeUiEvent
 import com.android.systemui.truth.correspondence.LogMaker
-import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.argumentCaptor
-import com.android.systemui.util.mockito.eq
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.nullable
 import com.google.common.truth.Truth.assertThat
@@ -45,8 +43,11 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyInt
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.`when` as whenever
+import org.mockito.kotlin.any
+import org.mockito.kotlin.eq
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
@@ -92,9 +93,10 @@
         // Dialog is shown.
         verify(globalActionsDialogLite)
             .showOrHideDialog(
-                /* keyguardShowing= */ false,
-                /* isDeviceProvisioned= */ true,
-                expandable,
+                /* keyguardShowing= */ eq(false),
+                /* isDeviceProvisioned= */ eq(true),
+                eq(expandable),
+                anyInt(),
             )
     }
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/StockTilesRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/StockTilesRepositoryTest.kt
index 56cead1..0d2f4ee 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/StockTilesRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/StockTilesRepositoryTest.kt
@@ -17,8 +17,11 @@
 package com.android.systemui.qs.panels.data.repository
 
 import android.content.res.mainResources
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
+import com.android.server.display.feature.flags.Flags
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.qs.pipeline.shared.TileSpec
 import com.android.systemui.res.R
@@ -30,12 +33,49 @@
 @RunWith(AndroidJUnit4::class)
 @SmallTest
 class StockTilesRepositoryTest : SysuiTestCase() {
-    private val kosmos = testKosmos()
-
-    private val underTest = StockTilesRepository(kosmos.mainResources)
+    private val kosmos =
+        testKosmos().apply { mainResources = mContext.orCreateTestableResources.resources }
 
     @Test
-    fun stockTilesMatchesResources() {
+    @EnableFlags(Flags.FLAG_EVEN_DIMMER)
+    fun stockTilesMatchesResources_evenDimmerFlagOn_configOn() {
+        // Enable the EvenDimmer config
+        mContext
+            .getOrCreateTestableResources()
+            .addOverride(com.android.internal.R.bool.config_evenDimmerEnabled, true)
+        val underTest = StockTilesRepository(kosmos.mainResources)
+
+        val expected =
+            kosmos.mainResources
+                .getString(R.string.quick_settings_tiles_stock)
+                .split(",")
+                .filterNot { it.equals("reduce_brightness") }
+                .map(TileSpec::create)
+        assertThat(underTest.stockTiles).isEqualTo(expected)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_EVEN_DIMMER)
+    fun stockTilesMatchesResources_evenDimmerFlagOn_configOff() {
+        // Disable the EvenDimmer config
+        mContext
+            .getOrCreateTestableResources()
+            .addOverride(com.android.internal.R.bool.config_evenDimmerEnabled, false)
+        val underTest = StockTilesRepository(kosmos.mainResources)
+
+        val expected =
+            kosmos.mainResources
+                .getString(R.string.quick_settings_tiles_stock)
+                .split(",")
+                .map(TileSpec::create)
+        assertThat(underTest.stockTiles).isEqualTo(expected)
+    }
+
+    @Test
+    @DisableFlags(Flags.FLAG_EVEN_DIMMER)
+    fun stockTilesMatchesResources_evenDimmerFlagOff() {
+        val underTest = StockTilesRepository(kosmos.mainResources)
+
         val expected =
             kosmos.mainResources
                 .getString(R.string.quick_settings_tiles_stock)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/CastTileTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/CastTileTest.java
index 31a627f..9f12b18 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/CastTileTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/CastTileTest.java
@@ -20,7 +20,6 @@
 import static junit.framework.TestCase.assertEquals;
 
 import static org.junit.Assert.assertFalse;
-import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.ArgumentMatchers.same;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.mock;
@@ -31,7 +30,6 @@
 import android.media.MediaRouter;
 import android.media.MediaRouter.RouteInfo;
 import android.media.projection.MediaProjectionInfo;
-import android.media.projection.StopReason;
 import android.os.Handler;
 import android.service.quicksettings.Tile;
 import android.testing.TestableLooper;
@@ -338,8 +336,7 @@
         mCastTile.handleClick(null /* view */);
         mTestableLooper.processAllMessages();
 
-        verify(mController, times(1))
-                .stopCasting(same(device), eq(StopReason.STOP_QS_TILE));
+        verify(mController, times(1)).stopCasting(same(device));
     }
 
     @Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/DataSaverTileTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/DataSaverTileTest.kt
index 27fd281..fbbdc46 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/DataSaverTileTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/DataSaverTileTest.kt
@@ -46,6 +46,7 @@
 import org.junit.runner.RunWith
 import org.mockito.Mock
 import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.eq
 import platform.test.runner.parameterized.ParameterizedAndroidJunit4
 import platform.test.runner.parameterized.Parameters
 
@@ -79,7 +80,7 @@
         testableLooper = TestableLooper.get(this)
 
         whenever(mHost.context).thenReturn(mContext)
-        whenever(systemUIDialogFactory.create()).thenReturn(systemUIDialog)
+        whenever(systemUIDialogFactory.create(eq(mContext))).thenReturn(systemUIDialog)
 
         tile =
             DataSaverTile(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt
index 9e4cf94..0bb86da 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt
@@ -141,7 +141,7 @@
             if (featureEnabled) {
                 Optional.of(controlsController)
             } else {
-                Optional.empty()
+                Optional.empty<ControlsController>()
             }
         }
 
@@ -149,7 +149,7 @@
             if (featureEnabled) {
                 Optional.of(controlsListingController)
             } else {
-                Optional.empty()
+                Optional.empty<ControlsController>()
             }
         }
 
@@ -157,7 +157,7 @@
             if (featureEnabled) {
                 Optional.of(controlsUiController)
             } else {
-                Optional.empty()
+                Optional.empty<ControlsController>()
             }
         }
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java
index fc1d73b..0fd7c98 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java
@@ -27,13 +27,11 @@
 import static org.junit.Assert.assertFalse;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.app.Dialog;
-import android.media.projection.StopReason;
 import android.os.Handler;
 import android.platform.test.flag.junit.FlagsParameterization;
 import android.service.quicksettings.Tile;
@@ -236,7 +234,7 @@
 
         mTile.handleClick(null /* view */);
 
-        verify(mController, times(1)).stopRecording(eq(StopReason.STOP_QS_TILE));
+        verify(mController, times(1)).stopRecording();
     }
 
     @Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractorTest.kt
index 778c73f..0b56d7b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractorTest.kt
@@ -17,7 +17,6 @@
 package com.android.systemui.qs.tiles.impl.screenrecord.domain.interactor
 
 import android.app.Dialog
-import android.media.projection.StopReason
 import android.os.UserHandle
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
@@ -93,7 +92,7 @@
 
         underTest.handleInput(QSTileInputTestKtx.click(recordingModel))
 
-        verify(recordingController).stopRecording(eq(StopReason.STOP_QS_TILE))
+        verify(recordingController).stopRecording()
     }
 
     @Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/user/UserSwitchDialogControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/user/UserSwitchDialogControllerTest.kt
index ad6c64b..039a1dc 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/user/UserSwitchDialogControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/user/UserSwitchDialogControllerTest.kt
@@ -32,7 +32,6 @@
 import com.android.systemui.qs.QSUserSwitcherEvent
 import com.android.systemui.qs.tiles.UserDetailView
 import com.android.systemui.statusbar.phone.SystemUIDialog
-import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.capture
 import com.android.systemui.util.mockito.eq
 import com.android.systemui.util.mockito.mock
@@ -51,31 +50,22 @@
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.`when`
 import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.any
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class UserSwitchDialogControllerTest : SysuiTestCase() {
 
-    @Mock
-    private lateinit var dialogFactory: SystemUIDialog.Factory
-    @Mock
-    private lateinit var dialog: SystemUIDialog
-    @Mock
-    private lateinit var falsingManager: FalsingManager
-    @Mock
-    private lateinit var activityStarter: ActivityStarter
-    @Mock
-    private lateinit var userDetailViewAdapter: UserDetailView.Adapter
-    @Mock
-    private lateinit var launchExpandable: Expandable
-    @Mock
-    private lateinit var neutralButton: Button
-    @Mock
-    private lateinit var mDialogTransitionAnimator: DialogTransitionAnimator
-    @Mock
-    private lateinit var uiEventLogger: UiEventLogger
-    @Captor
-    private lateinit var clickCaptor: ArgumentCaptor<DialogInterface.OnClickListener>
+    @Mock private lateinit var dialogFactory: SystemUIDialog.Factory
+    @Mock private lateinit var dialog: SystemUIDialog
+    @Mock private lateinit var falsingManager: FalsingManager
+    @Mock private lateinit var activityStarter: ActivityStarter
+    @Mock private lateinit var userDetailViewAdapter: UserDetailView.Adapter
+    @Mock private lateinit var launchExpandable: Expandable
+    @Mock private lateinit var neutralButton: Button
+    @Mock private lateinit var mDialogTransitionAnimator: DialogTransitionAnimator
+    @Mock private lateinit var uiEventLogger: UiEventLogger
+    @Captor private lateinit var clickCaptor: ArgumentCaptor<DialogInterface.OnClickListener>
 
     private lateinit var controller: UserSwitchDialogController
 
@@ -84,16 +74,17 @@
         MockitoAnnotations.initMocks(this)
 
         whenever(dialog.context).thenReturn(mContext)
-        whenever(dialogFactory.create()).thenReturn(dialog)
+        whenever(dialogFactory.create(eq(mContext))).thenReturn(dialog)
 
-        controller = UserSwitchDialogController(
-            { userDetailViewAdapter },
-            activityStarter,
-            falsingManager,
-            mDialogTransitionAnimator,
-            uiEventLogger,
-            dialogFactory
-        )
+        controller =
+            UserSwitchDialogController(
+                { userDetailViewAdapter },
+                activityStarter,
+                falsingManager,
+                mDialogTransitionAnimator,
+                uiEventLogger,
+                dialogFactory,
+            )
     }
 
     @Test
@@ -150,7 +141,7 @@
             .postStartActivityDismissingKeyguard(
                 argThat(IntentMatcher(Settings.ACTION_USER_SETTINGS)),
                 eq(0),
-                eq(null)
+                eq(null),
             )
         verify(uiEventLogger).log(QSUserSwitcherEvent.QS_USER_MORE_SETTINGS)
     }
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 79bb0c4..06dd046 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
@@ -74,6 +74,7 @@
 import com.android.systemui.keyguard.data.repository.keyguardRepository
 import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
 import com.android.systemui.keyguard.dismissCallbackRegistry
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.keyguard.domain.interactor.dozeInteractor
 import com.android.systemui.keyguard.domain.interactor.keyguardEnabledInteractor
 import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
@@ -1270,8 +1271,11 @@
                 authenticationMethod = AuthenticationMethodModel.None,
                 isLockscreenEnabled = false,
             )
-            assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
+            powerInteractor.setAsleepForTest()
             underTest.start()
+            runCurrent()
+            assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
+
             powerInteractor.setAwakeForTest()
             runCurrent()
 
@@ -2139,6 +2143,7 @@
             val currentScene by collectLastValue(sceneInteractor.currentScene)
             val transitionStateFlow = prepareState()
             underTest.start()
+            runCurrent()
             emulateSceneTransition(transitionStateFlow, toScene = Scenes.Bouncer)
             assertThat(currentScene).isNotEqualTo(Scenes.Lockscreen)
 
@@ -2153,6 +2158,7 @@
             val currentScene by collectLastValue(sceneInteractor.currentScene)
             val transitionStateFlow = prepareState()
             underTest.start()
+            runCurrent()
             emulateSceneTransition(transitionStateFlow, toScene = Scenes.Bouncer)
             assertThat(currentScene).isEqualTo(Scenes.Bouncer)
 
@@ -2269,6 +2275,7 @@
             assertThat(currentScene).isEqualTo(Scenes.Gone)
             assertThat(kosmos.deviceEntryInteractor.isDeviceEntered.value).isTrue()
             underTest.start()
+            runCurrent()
             sceneInteractor.changeScene(Scenes.Shade, "")
             assertThat(currentScene).isEqualTo(Scenes.Shade)
             assertThat(kosmos.deviceEntryInteractor.isDeviceEntered.value).isTrue()
@@ -2350,6 +2357,7 @@
             val currentScene by collectLastValue(sceneInteractor.currentScene)
             prepareState()
             underTest.start()
+            runCurrent()
 
             // run all pending dismiss succeeded/cancelled calls from setup:
             kosmos.fakeExecutor.runAllReady()
@@ -2461,13 +2469,18 @@
     @Test
     fun switchFromDreamToLockscreen_whenLockedAndDreamStopped() =
         testScope.runTest {
-            keyguardInteractor.setDreaming(true)
             val currentScene by collectLastValue(sceneInteractor.currentScene)
             prepareState(initialSceneKey = Scenes.Dream)
-            assertThat(currentScene).isEqualTo(Scenes.Dream)
             underTest.start()
+            advanceTimeBy(KeyguardInteractor.IS_ABLE_TO_DREAM_DELAY_MS)
+            runCurrent()
+            keyguardInteractor.setDreaming(true)
+            advanceTimeBy(KeyguardInteractor.IS_ABLE_TO_DREAM_DELAY_MS)
+            runCurrent()
+            assertThat(currentScene).isEqualTo(Scenes.Dream)
 
             keyguardInteractor.setDreaming(false)
+            advanceTimeBy(KeyguardInteractor.IS_ABLE_TO_DREAM_DELAY_MS)
             runCurrent()
             assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
         }
@@ -2475,13 +2488,18 @@
     @Test
     fun switchFromDreamToGone_whenUnlockedAndDreamStopped() =
         testScope.runTest {
-            keyguardInteractor.setDreaming(true)
             val currentScene by collectLastValue(sceneInteractor.currentScene)
             prepareState(initialSceneKey = Scenes.Dream, isDeviceUnlocked = true)
-            assertThat(currentScene).isEqualTo(Scenes.Dream)
             underTest.start()
+            advanceTimeBy(KeyguardInteractor.IS_ABLE_TO_DREAM_DELAY_MS)
+            runCurrent()
+            keyguardInteractor.setDreaming(true)
+            advanceTimeBy(KeyguardInteractor.IS_ABLE_TO_DREAM_DELAY_MS)
+            runCurrent()
+            assertThat(currentScene).isEqualTo(Scenes.Dream)
 
             keyguardInteractor.setDreaming(false)
+            advanceTimeBy(KeyguardInteractor.IS_ABLE_TO_DREAM_DELAY_MS)
             runCurrent()
             assertThat(currentScene).isEqualTo(Scenes.Gone)
         }
@@ -2684,6 +2702,7 @@
             underTest.start()
             val currentScene by collectLastValue(sceneInteractor.currentScene)
             val currentOverlays by collectLastValue(sceneInteractor.currentOverlays)
+            runCurrent()
             sceneInteractor.changeScene(Scenes.Shade, "reason")
             sceneInteractor.showOverlay(Overlays.NotificationsShade, "reason")
             assertThat(currentScene).isEqualTo(Scenes.Shade)
@@ -2835,8 +2854,15 @@
             )
         sceneInteractor.setTransitionState(transitionStateFlow)
         initialSceneKey?.let {
+            if (isDeviceUnlocked && initialSceneKey != Scenes.Gone) {
+                // Pass through the Gone scene to populate device entry state properly.
+                transitionStateFlow.value = ObservableTransitionState.Idle(Scenes.Gone)
+                sceneInteractor.changeScene(Scenes.Gone, "prepareState, passing through Gone scene")
+                runCurrent()
+            }
+
             transitionStateFlow.value = ObservableTransitionState.Idle(it)
-            sceneInteractor.changeScene(it, "reason")
+            sceneInteractor.changeScene(it, "prepareState, initialSceneKey isn't null")
         }
         if (startsAwake) {
             powerInteractor.setAwakeForTest()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagParameterizationTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagParameterizationTest.kt
index f86337e..396f531 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagParameterizationTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagParameterizationTest.kt
@@ -20,7 +20,7 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.Flags.FLAG_EXAMPLE_FLAG
-import com.android.systemui.Flags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR
+import com.android.systemui.Flags.FLAG_NOTIFICATION_AVALANCHE_THROTTLE_HUN
 import com.android.systemui.Flags.FLAG_SCENE_CONTAINER
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.flags.andSceneContainer
@@ -66,7 +66,7 @@
 
     @Test
     fun oneDependencyAndSceneContainer() {
-        val dependentFlag = FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR
+        val dependentFlag = FLAG_NOTIFICATION_AVALANCHE_THROTTLE_HUN
         val result = FlagsParameterization.allCombinationsOf(dependentFlag).andSceneContainer()
         Truth.assertThat(result).hasSize(3)
         Truth.assertThat(result[0].mOverrides[dependentFlag]).isFalse()
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 50fa9d2..a6a1d4a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/screenrecord/RecordingServiceTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenrecord/RecordingServiceTest.java
@@ -41,7 +41,6 @@
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.content.Intent;
-import android.media.projection.StopReason;
 import android.os.Handler;
 import android.os.RemoteException;
 import android.os.UserHandle;
@@ -200,16 +199,16 @@
     public void testOnSystemRequestedStop_recordingInProgress_endsRecording() throws IOException {
         doReturn(true).when(mController).isRecording();
 
-        mRecordingService.onStopped(StopReason.STOP_UNKNOWN);
+        mRecordingService.onStopped();
 
-        verify(mScreenMediaRecorder).end(eq(StopReason.STOP_UNKNOWN));
+        verify(mScreenMediaRecorder).end();
     }
 
     @Test
     public void testOnSystemRequestedStop_recordingInProgress_updatesState() {
         doReturn(true).when(mController).isRecording();
 
-        mRecordingService.onStopped(StopReason.STOP_UNKNOWN);
+        mRecordingService.onStopped();
 
         assertUpdateState(false);
     }
@@ -219,18 +218,18 @@
             throws IOException {
         doReturn(false).when(mController).isRecording();
 
-        mRecordingService.onStopped(StopReason.STOP_UNKNOWN);
+        mRecordingService.onStopped();
 
-        verify(mScreenMediaRecorder, never()).end(StopReason.STOP_UNKNOWN);
+        verify(mScreenMediaRecorder, never()).end();
     }
 
     @Test
     public void testOnSystemRequestedStop_recorderEndThrowsRuntimeException_releasesRecording()
             throws IOException {
         doReturn(true).when(mController).isRecording();
-        doThrow(new RuntimeException()).when(mScreenMediaRecorder).end(StopReason.STOP_UNKNOWN);
+        doThrow(new RuntimeException()).when(mScreenMediaRecorder).end();
 
-        mRecordingService.onStopped(StopReason.STOP_UNKNOWN);
+        mRecordingService.onStopped();
 
         verify(mScreenMediaRecorder).release();
     }
@@ -239,7 +238,7 @@
     public void testOnSystemRequestedStop_whenRecordingInProgress_showsNotifications() {
         doReturn(true).when(mController).isRecording();
 
-        mRecordingService.onStopped(StopReason.STOP_UNKNOWN);
+        mRecordingService.onStopped();
 
         // Processing notification
         ArgumentCaptor<Notification> notifCaptor = ArgumentCaptor.forClass(Notification.class);
@@ -272,9 +271,9 @@
     public void testOnSystemRequestedStop_recorderEndThrowsRuntimeException_showsErrorNotification()
             throws IOException {
         doReturn(true).when(mController).isRecording();
-        doThrow(new RuntimeException()).when(mScreenMediaRecorder).end(anyInt());
+        doThrow(new RuntimeException()).when(mScreenMediaRecorder).end();
 
-        mRecordingService.onStopped(StopReason.STOP_UNKNOWN);
+        mRecordingService.onStopped();
 
         verify(mRecordingService).createErrorSavingNotification(any());
         ArgumentCaptor<Notification> notifCaptor = ArgumentCaptor.forClass(Notification.class);
@@ -290,9 +289,9 @@
     public void testOnSystemRequestedStop_recorderEndThrowsOOMError_releasesRecording()
             throws IOException {
         doReturn(true).when(mController).isRecording();
-        doThrow(new OutOfMemoryError()).when(mScreenMediaRecorder).end(anyInt());
+        doThrow(new OutOfMemoryError()).when(mScreenMediaRecorder).end();
 
-        assertThrows(Throwable.class, () -> mRecordingService.onStopped(StopReason.STOP_UNKNOWN));
+        assertThrows(Throwable.class, () -> mRecordingService.onStopped());
 
         verify(mScreenMediaRecorder).release();
     }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/screenrecord/data/repository/ScreenRecordRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenrecord/data/repository/ScreenRecordRepositoryTest.kt
index ade5941..aceea90 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/screenrecord/data/repository/ScreenRecordRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenrecord/data/repository/ScreenRecordRepositoryTest.kt
@@ -16,7 +16,6 @@
 
 package com.android.systemui.screenrecord.data.repository
 
-import android.media.projection.StopReason
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
@@ -32,7 +31,6 @@
 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.verify
 import org.mockito.kotlin.whenever
@@ -128,8 +126,8 @@
     @Test
     fun stopRecording_invokesController() =
         testScope.runTest {
-            underTest.stopRecording(StopReason.STOP_PRIVACY_CHIP)
+            underTest.stopRecording()
 
-            verify(recordingController).stopRecording(eq(StopReason.STOP_PRIVACY_CHIP))
+            verify(recordingController).stopRecording()
         }
 }
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 0d8d57e..d3b5828 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
@@ -68,7 +68,6 @@
 import com.android.internal.logging.testing.UiEventLoggerFake;
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.internal.util.LatencyTracker;
-import com.android.keyguard.EmptyLockIconViewController;
 import com.android.keyguard.KeyguardClockSwitch;
 import com.android.keyguard.KeyguardClockSwitchController;
 import com.android.keyguard.KeyguardSliceViewController;
@@ -99,7 +98,6 @@
 import com.android.systemui.keyguard.KeyguardViewConfigurator;
 import com.android.systemui.keyguard.data.repository.FakeKeyguardClockRepository;
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository;
-import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor;
 import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor;
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory;
@@ -108,7 +106,6 @@
 import com.android.systemui.keyguard.ui.view.KeyguardRootView;
 import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel;
 import com.android.systemui.keyguard.ui.viewmodel.GoneToDreamingTransitionViewModel;
-import com.android.systemui.keyguard.ui.viewmodel.KeyguardBottomAreaViewModel;
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardTouchHandlingViewModel;
 import com.android.systemui.keyguard.ui.viewmodel.LockscreenToDreamingTransitionViewModel;
 import com.android.systemui.keyguard.ui.viewmodel.LockscreenToOccludedTransitionViewModel;
@@ -167,8 +164,6 @@
 import com.android.systemui.statusbar.phone.ConfigurationControllerImpl;
 import com.android.systemui.statusbar.phone.DozeParameters;
 import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
-import com.android.systemui.statusbar.phone.KeyguardBottomAreaView;
-import com.android.systemui.statusbar.phone.KeyguardBottomAreaViewController;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.phone.KeyguardClockPositionAlgorithm;
 import com.android.systemui.statusbar.phone.KeyguardStatusBarView;
@@ -228,10 +223,7 @@
 
     @Mock protected CentralSurfaces mCentralSurfaces;
     @Mock protected NotificationStackScrollLayout mNotificationStackScrollLayout;
-    @Mock protected KeyguardBottomAreaView mKeyguardBottomArea;
-    @Mock protected KeyguardBottomAreaViewController mKeyguardBottomAreaViewController;
     @Mock protected ViewPropertyAnimator mViewPropertyAnimator;
-    @Mock protected KeyguardBottomAreaView mQsFrame;
     @Mock protected HeadsUpManager mHeadsUpManager;
     @Mock protected NotificationGutsManager mGutsManager;
     @Mock protected KeyguardStatusBarView mKeyguardStatusBar;
@@ -270,7 +262,6 @@
     @Mock protected KeyguardUserSwitcherController mKeyguardUserSwitcherController;
     @Mock protected KeyguardStatusViewComponent mKeyguardStatusViewComponent;
     @Mock protected KeyguardStatusBarViewComponent.Factory mKeyguardStatusBarViewComponentFactory;
-    @Mock protected EmptyLockIconViewController mLockIconViewController;
     @Mock protected KeyguardStatusBarViewComponent mKeyguardStatusBarViewComponent;
     @Mock protected KeyguardClockSwitchController mKeyguardClockSwitchController;
     @Mock protected KeyguardStatusBarViewController mKeyguardStatusBarViewController;
@@ -317,7 +308,6 @@
     @Mock protected ViewGroup mQsHeader;
     @Mock protected ViewParent mViewParent;
     @Mock protected ViewTreeObserver mViewTreeObserver;
-    @Mock protected KeyguardBottomAreaViewModel mKeyguardBottomAreaViewModel;
     @Mock protected DreamingToLockscreenTransitionViewModel
             mDreamingToLockscreenTransitionViewModel;
     @Mock protected OccludedToLockscreenTransitionViewModel
@@ -352,7 +342,6 @@
     @Mock private StatusBarLongPressGestureDetector mStatusBarLongPressGestureDetector;
     protected final int mMaxUdfpsBurnInOffsetY = 5;
     protected FakeFeatureFlagsClassic mFeatureFlags = new FakeFeatureFlagsClassic();
-    protected KeyguardBottomAreaInteractor mKeyguardBottomAreaInteractor;
     protected KeyguardClockInteractor mKeyguardClockInteractor;
     protected FakeKeyguardRepository mFakeKeyguardRepository;
     protected FakeKeyguardClockRepository mFakeKeyguardClockRepository;
@@ -397,13 +386,10 @@
         mFeatureFlags.set(Flags.LOCKSCREEN_ENABLE_LANDSCAPE, false);
         mFeatureFlags.set(Flags.QS_USER_DETAIL_SHORTCUT, false);
 
-        mSetFlagsRule.disableFlags(com.android.systemui.Flags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR);
-
         mMainDispatcher = getMainDispatcher();
         KeyguardInteractorFactory.WithDependencies keyguardInteractorDeps =
                 KeyguardInteractorFactory.create();
         mFakeKeyguardRepository = keyguardInteractorDeps.getRepository();
-        mKeyguardBottomAreaInteractor = new KeyguardBottomAreaInteractor(mFakeKeyguardRepository);
         mFakeKeyguardClockRepository = new FakeKeyguardClockRepository();
         mKeyguardClockInteractor = mKosmos.getKeyguardClockInteractor();
         mKeyguardInteractor = keyguardInteractorDeps.getKeyguardInteractor();
@@ -500,9 +486,6 @@
         when(mNotificationStackScrollLayoutController.getHeight()).thenReturn(1000);
         when(mNotificationStackScrollLayoutController.getHeadsUpCallback())
                 .thenReturn(mHeadsUpCallback);
-        when(mKeyguardBottomAreaViewController.getView()).thenReturn(mKeyguardBottomArea);
-        when(mView.findViewById(R.id.keyguard_bottom_area)).thenReturn(mKeyguardBottomArea);
-        when(mKeyguardBottomArea.animate()).thenReturn(mViewPropertyAnimator);
         when(mView.animate()).thenReturn(mViewPropertyAnimator);
         when(mKeyguardStatusView.animate()).thenReturn(mViewPropertyAnimator);
         when(mViewPropertyAnimator.translationX(anyFloat())).thenReturn(mViewPropertyAnimator);
@@ -513,7 +496,6 @@
         when(mViewPropertyAnimator.setListener(any())).thenReturn(mViewPropertyAnimator);
         when(mViewPropertyAnimator.setUpdateListener(any())).thenReturn(mViewPropertyAnimator);
         when(mViewPropertyAnimator.withEndAction(any())).thenReturn(mViewPropertyAnimator);
-        when(mView.findViewById(R.id.qs_frame)).thenReturn(mQsFrame);
         when(mView.findViewById(R.id.keyguard_status_view))
                 .thenReturn(mock(KeyguardStatusView.class));
         ViewGroup rootView = mock(ViewGroup.class);
@@ -647,8 +629,6 @@
                 .thenReturn(keyguardStatusView);
         when(mLayoutInflater.inflate(eq(R.layout.keyguard_user_switcher), any(), anyBoolean()))
                 .thenReturn(mUserSwitcherView);
-        when(mLayoutInflater.inflate(eq(R.layout.keyguard_bottom_area), any(), anyBoolean()))
-                .thenReturn(mKeyguardBottomArea);
         when(mNotificationRemoteInputManager.isRemoteInputActive())
                 .thenReturn(false);
         doAnswer(invocation -> {
@@ -720,7 +700,6 @@
                 mMediaDataManager,
                 mNotificationShadeDepthController,
                 mAmbientState,
-                mLockIconViewController,
                 mKeyguardMediaController,
                 mTapAgainViewController,
                 mNavigationModeController,
@@ -736,15 +715,12 @@
                 mShadeRepository,
                 mSysUIUnfoldComponent,
                 mSysUiState,
-                () -> mKeyguardBottomAreaViewController,
                 mKeyguardUnlockAnimationController,
                 mKeyguardIndicationController,
                 mNotificationListContainer,
                 mNotificationStackSizeCalculator,
                 mUnlockedScreenOffAnimationController,
                 systemClock,
-                mKeyguardBottomAreaViewModel,
-                mKeyguardBottomAreaInteractor,
                 mKeyguardClockInteractor,
                 mAlternateBouncerInteractor,
                 mDreamingToLockscreenTransitionViewModel,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerWithCoroutinesTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerWithCoroutinesTest.kt
index 97441f0..5289554 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerWithCoroutinesTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerWithCoroutinesTest.kt
@@ -28,7 +28,6 @@
 import com.android.internal.util.CollectionUtils
 import com.android.keyguard.KeyguardClockSwitch.LARGE
 import com.android.systemui.Flags
-import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.StatusBarState.KEYGUARD
 import com.android.systemui.statusbar.StatusBarState.SHADE
@@ -215,31 +214,4 @@
         }
         advanceUntilIdle()
     }
-
-    @Test
-    fun onLayoutChange_shadeCollapsed_bottomAreaAlphaIsZero() = runTest {
-        // GIVEN bottomAreaShadeAlpha was updated before
-        mNotificationPanelViewController.maybeAnimateBottomAreaAlpha()
-
-        // WHEN a layout change is triggered with the shade being closed
-        triggerLayoutChange()
-
-        // THEN the bottomAreaAlpha is zero
-        val bottomAreaAlpha by collectLastValue(mFakeKeyguardRepository.bottomAreaAlpha)
-        assertThat(bottomAreaAlpha).isEqualTo(0f)
-    }
-
-    @Test
-    fun onShadeExpanded_bottomAreaAlphaIsFullyOpaque() = runTest {
-        // GIVEN bottomAreaShadeAlpha was updated before
-        mNotificationPanelViewController.maybeAnimateBottomAreaAlpha()
-
-        // WHEN the shade expanded
-        val transitionDistance = mNotificationPanelViewController.maxPanelTransitionDistance
-        mNotificationPanelViewController.expandedHeight = transitionDistance.toFloat()
-
-        // THEN the bottomAreaAlpha is fully opaque
-        val bottomAreaAlpha by collectLastValue(mFakeKeyguardRepository.bottomAreaAlpha)
-        assertThat(bottomAreaAlpha).isEqualTo(1f)
-    }
 }
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 5d1ce7c5..929537dc 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
@@ -253,6 +253,16 @@
         verify(configurationForwarder).onConfigurationChanged(eq(config))
     }
 
+    @Test
+    @EnableFlags(AConfigFlags.FLAG_SHADE_WINDOW_GOES_AROUND)
+    fun onMovedToDisplay_configForwarderSet_propagatesConfig() {
+        val config = Configuration()
+
+        underTest.onMovedToDisplay(1, config)
+
+        verify(configurationForwarder).dispatchOnMovedToDisplay(eq(1), eq(config))
+    }
+
     private fun captureInteractionEventHandler() {
         verify(underTest).setInteractionEventHandler(interactionEventHandlerCaptor.capture())
         interactionEventHandler = interactionEventHandlerCaptor.value
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepositoryTest.kt
index 0966759..007a0fb 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepositoryTest.kt
@@ -16,17 +16,20 @@
 
 package com.android.systemui.shade.data.repository
 
+import android.provider.Settings.Global.DEVELOPMENT_SHADE_DISPLAY_AWARENESS
 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.coroutines.collectValues
+import com.android.systemui.display.data.repository.displayRepository
 import com.android.systemui.kosmos.testScope
-import com.android.systemui.shade.display.ShadeDisplayPolicy
-import com.android.systemui.shade.display.SpecificDisplayIdPolicy
+import com.android.systemui.shade.display.AnyExternalShadeDisplayPolicy
+import com.android.systemui.shade.display.DefaultDisplayShadePolicy
+import com.android.systemui.shade.display.StatusBarTouchShadeDisplayPolicy
 import com.android.systemui.testKosmos
+import com.android.systemui.util.settings.fakeGlobalSettings
 import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.test.runTest
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -36,54 +39,72 @@
 class ShadeDisplaysRepositoryTest : SysuiTestCase() {
     private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
-    private val defaultPolicy = SpecificDisplayIdPolicy(0)
+    private val globalSettings = kosmos.fakeGlobalSettings
+    private val displayRepository = kosmos.displayRepository
+    private val defaultPolicy = DefaultDisplayShadePolicy()
+    private val policies = kosmos.shadeDisplayPolicies
 
-    private val shadeDisplaysRepository =
-        ShadeDisplaysRepositoryImpl(defaultPolicy, testScope.backgroundScope)
+    private val underTest =
+        ShadeDisplaysRepositoryImpl(
+            globalSettings,
+            defaultPolicy,
+            testScope.backgroundScope,
+            policies,
+        )
 
     @Test
     fun policy_changing_propagatedFromTheLatestPolicy() =
         testScope.runTest {
-            val displayIds by collectValues(shadeDisplaysRepository.displayId)
-            val policy1 = MutablePolicy()
-            val policy2 = MutablePolicy()
+            val displayIds by collectValues(underTest.displayId)
 
             assertThat(displayIds).containsExactly(0)
 
-            shadeDisplaysRepository.policy.value = policy1
+            globalSettings.putString(DEVELOPMENT_SHADE_DISPLAY_AWARENESS, "any_external_display")
 
-            policy1.sendDisplayId(1)
+            displayRepository.addDisplay(displayId = 1)
 
             assertThat(displayIds).containsExactly(0, 1)
 
-            policy1.sendDisplayId(2)
+            displayRepository.addDisplay(displayId = 2)
+
+            assertThat(displayIds).containsExactly(0, 1)
+
+            displayRepository.removeDisplay(displayId = 1)
 
             assertThat(displayIds).containsExactly(0, 1, 2)
 
-            shadeDisplaysRepository.policy.value = policy2
+            globalSettings.putString(DEVELOPMENT_SHADE_DISPLAY_AWARENESS, "default_display")
 
             assertThat(displayIds).containsExactly(0, 1, 2, 0)
-
-            policy1.sendDisplayId(4)
-
-            // Changes to the first policy don't affect the output now
-            assertThat(displayIds).containsExactly(0, 1, 2, 0)
-
-            policy2.sendDisplayId(5)
-
-            assertThat(displayIds).containsExactly(0, 1, 2, 0, 5)
         }
 
-    private class MutablePolicy : ShadeDisplayPolicy {
-        fun sendDisplayId(id: Int) {
-            _displayId.value = id
+    @Test
+    fun policy_updatesBasedOnSettingValue_defaultDisplay() =
+        testScope.runTest {
+            val policy by collectLastValue(underTest.policy)
+
+            globalSettings.putString(DEVELOPMENT_SHADE_DISPLAY_AWARENESS, "default_display")
+
+            assertThat(policy).isInstanceOf(DefaultDisplayShadePolicy::class.java)
         }
 
-        private val _displayId = MutableStateFlow(0)
-        override val name: String
-            get() = "mutable_policy"
+    @Test
+    fun policy_updatesBasedOnSettingValue_anyExternal() =
+        testScope.runTest {
+            val policy by collectLastValue(underTest.policy)
 
-        override val displayId: StateFlow<Int>
-            get() = _displayId
-    }
+            globalSettings.putString(DEVELOPMENT_SHADE_DISPLAY_AWARENESS, "any_external_display")
+
+            assertThat(policy).isInstanceOf(AnyExternalShadeDisplayPolicy::class.java)
+        }
+
+    @Test
+    fun policy_updatesBasedOnSettingValue_focusBased() =
+        testScope.runTest {
+            val policy by collectLastValue(underTest.policy)
+
+            globalSettings.putString(DEVELOPMENT_SHADE_DISPLAY_AWARENESS, "status_bar_latest_touch")
+
+            assertThat(policy).isInstanceOf(StatusBarTouchShadeDisplayPolicy::class.java)
+        }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadePrimaryDisplayCommandTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadePrimaryDisplayCommandTest.kt
index d584dc9..eeb3e6b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadePrimaryDisplayCommandTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadePrimaryDisplayCommandTest.kt
@@ -28,6 +28,7 @@
 import com.android.systemui.shade.display.ShadeDisplayPolicy
 import com.android.systemui.statusbar.commandline.commandRegistry
 import com.android.systemui.testKosmos
+import com.android.systemui.util.settings.fakeGlobalSettings
 import com.google.common.truth.StringSubject
 import com.google.common.truth.Truth.assertThat
 import java.io.PrintWriter
@@ -44,18 +45,17 @@
 class ShadePrimaryDisplayCommandTest : SysuiTestCase() {
     private val kosmos = testKosmos().useUnconfinedTestDispatcher()
     private val testScope = kosmos.testScope
+    private val globalSettings = kosmos.fakeGlobalSettings
     private val commandRegistry = kosmos.commandRegistry
     private val displayRepository = kosmos.displayRepository
     private val defaultPolicy = kosmos.defaultShadeDisplayPolicy
-    private val policy1 = makePolicy("policy_1")
     private val shadeDisplaysRepository = kosmos.shadeDisplaysRepository
+    private val policies = kosmos.shadeDisplayPolicies
     private val pw = PrintWriter(StringWriter())
 
-    private val policies =
-        setOf(defaultPolicy, policy1, makePolicy("policy_2"), makePolicy("policy_3"))
-
     private val underTest =
         ShadePrimaryDisplayCommand(
+            globalSettings,
             commandRegistry,
             displayRepository,
             shadeDisplaysRepository,
@@ -69,30 +69,16 @@
     }
 
     @Test
-    fun commandDisplayOverride_updatesDisplayId() =
-        testScope.runTest {
-            val displayId by collectLastValue(shadeDisplaysRepository.displayId)
-            assertThat(displayId).isEqualTo(Display.DEFAULT_DISPLAY)
-
-            val newDisplayId = 2
-            commandRegistry.onShellCommand(
-                pw,
-                arrayOf("shade_display_override", newDisplayId.toString()),
-            )
-
-            assertThat(displayId).isEqualTo(newDisplayId)
-        }
-
-    @Test
     fun commandShadeDisplayOverride_resetsDisplayId() =
         testScope.runTest {
             val displayId by collectLastValue(shadeDisplaysRepository.displayId)
             assertThat(displayId).isEqualTo(Display.DEFAULT_DISPLAY)
 
             val newDisplayId = 2
+            displayRepository.addDisplay(displayId = newDisplayId)
             commandRegistry.onShellCommand(
                 pw,
-                arrayOf("shade_display_override", newDisplayId.toString()),
+                arrayOf("shade_display_override", "any_external_display"),
             )
             assertThat(displayId).isEqualTo(newDisplayId)
 
@@ -108,7 +94,10 @@
             val newDisplayId = 2
             displayRepository.addDisplay(displayId = newDisplayId)
 
-            commandRegistry.onShellCommand(pw, arrayOf("shade_display_override", "any_external"))
+            commandRegistry.onShellCommand(
+                pw,
+                arrayOf("shade_display_override", "any_external_display"),
+            )
 
             assertThat(displayId).isEqualTo(newDisplayId)
         }
@@ -127,13 +116,14 @@
         }
 
     @Test
-    fun policies_setsSpecificPolicy() =
+    fun policies_setsNewPolicy() =
         testScope.runTest {
             val policy by collectLastValue(shadeDisplaysRepository.policy)
+            val newPolicy = policies.last().name
 
-            commandRegistry.onShellCommand(pw, arrayOf("shade_display_override", policy1.name))
+            commandRegistry.onShellCommand(pw, arrayOf("shade_display_override", newPolicy))
 
-            assertThat(policy!!.name).isEqualTo(policy1.name)
+            assertThat(policy!!.name).isEqualTo(newPolicy)
         }
 
     private fun makePolicy(policyName: String): ShadeDisplayPolicy {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
index 5b0b59d..4a3be44 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
@@ -31,6 +31,10 @@
 import static android.provider.Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS;
 import static android.provider.Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS;
 
+import static com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTION_TYPE_NONE;
+import static com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTION_TYPE_PUBLIC;
+import static com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTION_TYPE_SENSITIVE_CONTENT;
+
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertTrue;
@@ -280,8 +284,10 @@
 
         mBackgroundExecutor.runAllReady();
 
-        assertTrue(mLockscreenUserManager.needsRedaction(mCurrentUserNotif));
-        assertTrue(mLockscreenUserManager.needsRedaction(mSecondaryUserNotif));
+        assertEquals(REDACTION_TYPE_PUBLIC,
+                mLockscreenUserManager.getRedactionType(mCurrentUserNotif));
+        assertEquals(REDACTION_TYPE_PUBLIC,
+                mLockscreenUserManager.getRedactionType(mSecondaryUserNotif));
     }
 
     @Test
@@ -357,7 +363,8 @@
         changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
 
         // THEN current user's notification is redacted
-        assertTrue(mLockscreenUserManager.needsRedaction(mCurrentUserNotif));
+        assertEquals(REDACTION_TYPE_PUBLIC,
+                mLockscreenUserManager.getRedactionType(mCurrentUserNotif));
     }
 
     @Test
@@ -368,7 +375,8 @@
         changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
 
         // THEN current user's notification isn't redacted
-        assertFalse(mLockscreenUserManager.needsRedaction(mCurrentUserNotif));
+        assertEquals(REDACTION_TYPE_NONE,
+                mLockscreenUserManager.getRedactionType(mCurrentUserNotif));
     }
 
     @Test
@@ -385,7 +393,8 @@
                 .setChannel(channel)
                 .setVisibilityOverride(VISIBILITY_NO_OVERRIDE).build());
         // THEN the notification is redacted
-        assertTrue(mLockscreenUserManager.needsRedaction(mCurrentUserNotif));
+        assertEquals(REDACTION_TYPE_PUBLIC,
+                mLockscreenUserManager.getRedactionType(mCurrentUserNotif));
     }
 
     @Test
@@ -399,7 +408,8 @@
                 .setChannel(null)
                 .setVisibilityOverride(VISIBILITY_NO_OVERRIDE).build());
         // THEN the notification is not redacted
-        assertFalse(mLockscreenUserManager.needsRedaction(mCurrentUserNotif));
+        assertEquals(REDACTION_TYPE_NONE,
+                mLockscreenUserManager.getRedactionType(mCurrentUserNotif));
     }
 
     @Test
@@ -410,7 +420,8 @@
         changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
 
         // THEN work profile notification is redacted
-        assertTrue(mLockscreenUserManager.needsRedaction(mWorkProfileNotif));
+        assertEquals(REDACTION_TYPE_PUBLIC,
+                mLockscreenUserManager.getRedactionType(mWorkProfileNotif));
         assertFalse(mLockscreenUserManager.allowsManagedPrivateNotificationsInPublic());
     }
 
@@ -422,7 +433,8 @@
         changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
 
         // THEN work profile notification isn't redacted
-        assertFalse(mLockscreenUserManager.needsRedaction(mWorkProfileNotif));
+        assertEquals(REDACTION_TYPE_NONE,
+                mLockscreenUserManager.getRedactionType(mWorkProfileNotif));
         assertTrue(mLockscreenUserManager.allowsManagedPrivateNotificationsInPublic());
     }
 
@@ -440,11 +452,14 @@
         changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
 
         // THEN the work profile notification doesn't need to be redacted
-        assertFalse(mLockscreenUserManager.needsRedaction(mWorkProfileNotif));
+        assertEquals(REDACTION_TYPE_NONE,
+                mLockscreenUserManager.getRedactionType(mWorkProfileNotif));
 
         // THEN the current user and secondary user notifications do need to be redacted
-        assertTrue(mLockscreenUserManager.needsRedaction(mCurrentUserNotif));
-        assertTrue(mLockscreenUserManager.needsRedaction(mSecondaryUserNotif));
+        assertEquals(REDACTION_TYPE_PUBLIC,
+                mLockscreenUserManager.getRedactionType(mCurrentUserNotif));
+        assertEquals(REDACTION_TYPE_PUBLIC,
+                mLockscreenUserManager.getRedactionType(mSecondaryUserNotif));
     }
 
     @Test
@@ -461,11 +476,14 @@
         changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
 
         // THEN the work profile notification needs to be redacted
-        assertTrue(mLockscreenUserManager.needsRedaction(mWorkProfileNotif));
+        assertEquals(REDACTION_TYPE_PUBLIC,
+                mLockscreenUserManager.getRedactionType(mWorkProfileNotif));
 
         // THEN the current user and secondary user notifications don't need to be redacted
-        assertFalse(mLockscreenUserManager.needsRedaction(mCurrentUserNotif));
-        assertFalse(mLockscreenUserManager.needsRedaction(mSecondaryUserNotif));
+        assertEquals(REDACTION_TYPE_NONE,
+                mLockscreenUserManager.getRedactionType(mCurrentUserNotif));
+        assertEquals(REDACTION_TYPE_NONE,
+                mLockscreenUserManager.getRedactionType(mSecondaryUserNotif));
     }
 
     @Test
@@ -481,18 +499,20 @@
 
         // THEN the secondary profile notification still needs to be redacted because the current
         // user's setting takes precedence
-        assertTrue(mLockscreenUserManager.needsRedaction(mSecondaryUserNotif));
+        assertEquals(REDACTION_TYPE_PUBLIC,
+                mLockscreenUserManager.getRedactionType(mSecondaryUserNotif));
     }
 
     @Test
     public void testHasSensitiveContent_redacted() {
         // Allow private notifications for this user
-        mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0,
+        mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1,
                 mCurrentUser.id);
         changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
 
         // Sensitive Content notifications are always redacted
-        assertTrue(mLockscreenUserManager.needsRedaction(mSensitiveContentNotif));
+        assertEquals(REDACTION_TYPE_SENSITIVE_CONTENT,
+                mLockscreenUserManager.getRedactionType(mSensitiveContentNotif));
     }
 
     @Test
@@ -707,9 +727,11 @@
                 new Intent(ACTION_KEYGUARD_PRIVATE_NOTIFICATIONS_CHANGED)
                         .putExtra(EXTRA_KM_PRIVATE_NOTIFS_ALLOWED, true));
 
-        assertTrue(mLockscreenUserManager.needsRedaction(mCurrentUserNotif));
+        assertEquals(REDACTION_TYPE_PUBLIC,
+                mLockscreenUserManager.getRedactionType(mCurrentUserNotif));
         // it's a global field, confirm secondary too
-        assertTrue(mLockscreenUserManager.needsRedaction(mSecondaryUserNotif));
+        assertEquals(REDACTION_TYPE_PUBLIC,
+                mLockscreenUserManager.getRedactionType(mSecondaryUserNotif));
         assertFalse(mLockscreenUserManager.userAllowsPrivateNotificationsInPublic(mCurrentUser.id));
         assertFalse(mLockscreenUserManager.userAllowsPrivateNotificationsInPublic(
                 mSecondaryUser.id));
@@ -732,7 +754,8 @@
         mLockscreenUserManager.mAllUsersReceiver.onReceive(mContext,
                 new Intent(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED));
 
-        assertTrue(mLockscreenUserManager.needsRedaction(mCurrentUserNotif));
+        assertEquals(REDACTION_TYPE_PUBLIC,
+                mLockscreenUserManager.getRedactionType(mCurrentUserNotif));
 
         verify(mDevicePolicyManager, atMost(1)).getKeyguardDisabledFeatures(any(), anyInt());
     }
@@ -763,7 +786,7 @@
         mLockscreenUserManager.mAllUsersReceiver.onReceive(mContext,
                 new Intent(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED));
 
-        assertTrue(mLockscreenUserManager.needsRedaction(notifEntry));
+        assertEquals(REDACTION_TYPE_PUBLIC, mLockscreenUserManager.getRedactionType(notifEntry));
     }
 
     @Test
@@ -784,7 +807,8 @@
                 new Intent(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED));
 
         mLockscreenUserManager.mUserChangedCallback.onUserChanging(mSecondaryUser.id, mContext);
-        assertTrue(mLockscreenUserManager.needsRedaction(mSecondaryUserNotif));
+        assertEquals(REDACTION_TYPE_PUBLIC,
+                mLockscreenUserManager.getRedactionType(mSecondaryUserNotif));
 
         verify(mDevicePolicyManager, atMost(1)).getKeyguardDisabledFeatures(any(), anyInt());
     }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractorTest.kt
index 611318a..dea3d1f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractorTest.kt
@@ -197,7 +197,7 @@
                         if (
                             (it.arguments[0] as Intent).`package` == CAST_TO_OTHER_DEVICES_PACKAGE
                         ) {
-                            emptyList()
+                            emptyList<ResolveInfo>()
                         } else {
                             listOf(mock<ResolveInfo>())
                         }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractorTest.kt
index 63efc55..9ad1f40 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractorTest.kt
@@ -26,7 +26,6 @@
 import com.android.systemui.kosmos.runTest
 import com.android.systemui.kosmos.useUnconfinedTestDispatcher
 import com.android.systemui.statusbar.StatusBarIconView
-import com.android.systemui.statusbar.chips.notification.domain.model.NotificationChipModel
 import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
 import com.android.systemui.statusbar.notification.data.model.activeNotificationModel
 import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
@@ -50,7 +49,6 @@
                 activeNotificationModel(
                     key = "notif1",
                     statusBarChipIcon = icon,
-                    whenTime = 5432,
                     promotedContent = PROMOTED_CONTENT,
                 )
 
@@ -60,7 +58,7 @@
 
             assertThat(latest!!.key).isEqualTo("notif1")
             assertThat(latest!!.statusBarChipIconView).isEqualTo(icon)
-            assertThat(latest!!.whenTime).isEqualTo(5432)
+            assertThat(latest!!.promotedContent).isEqualTo(PROMOTED_CONTENT)
         }
 
     @Test
@@ -83,14 +81,12 @@
                 activeNotificationModel(
                     key = "notif1",
                     statusBarChipIcon = newIconView,
-                    whenTime = 6543,
                     promotedContent = PROMOTED_CONTENT,
                 )
             )
 
             assertThat(latest!!.key).isEqualTo("notif1")
             assertThat(latest!!.statusBarChipIconView).isEqualTo(newIconView)
-            assertThat(latest!!.whenTime).isEqualTo(6543)
         }
 
     @Test
@@ -174,22 +170,14 @@
                     activeNotificationModel(
                         key = "notif1",
                         statusBarChipIcon = null,
-                        whenTime = 123L,
                         promotedContent = PROMOTED_CONTENT,
                     )
                 )
 
             val latest by collectLastValue(underTest.notificationChip)
 
-            assertThat(latest)
-                .isEqualTo(
-                    NotificationChipModel(
-                        "notif1",
-                        statusBarChipIconView = null,
-                        whenTime = 123L,
-                        promotedContent = PROMOTED_CONTENT,
-                    )
-                )
+            assertThat(latest).isNotNull()
+            assertThat(latest!!.key).isEqualTo("notif1")
         }
 
     @Test
@@ -234,20 +222,12 @@
                 activeNotificationModel(
                     key = "notif1",
                     statusBarChipIcon = null,
-                    whenTime = 123L,
                     promotedContent = PROMOTED_CONTENT,
                 )
             )
 
-            assertThat(latest)
-                .isEqualTo(
-                    NotificationChipModel(
-                        key = "notif1",
-                        statusBarChipIconView = null,
-                        whenTime = 123L,
-                        promotedContent = PROMOTED_CONTENT,
-                    )
-                )
+            assertThat(latest).isNotNull()
+            assertThat(latest!!.key).isEqualTo("notif1")
         }
 
     @Test
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 b174fce..165e943 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
@@ -22,8 +22,11 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
 import com.android.systemui.kosmos.collectLastValue
 import com.android.systemui.kosmos.runTest
+import com.android.systemui.kosmos.testScope
 import com.android.systemui.kosmos.useUnconfinedTestDispatcher
 import com.android.systemui.statusbar.StatusBarIconView
 import com.android.systemui.statusbar.chips.notification.domain.interactor.statusBarNotificationChipsInteractor
@@ -33,12 +36,16 @@
 import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
 import com.android.systemui.statusbar.notification.data.model.activeNotificationModel
 import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore
+import com.android.systemui.statusbar.notification.data.repository.UnconfinedFakeHeadsUpRowRepository
 import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository
+import com.android.systemui.statusbar.notification.headsup.PinnedStatus
 import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
 import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel
+import com.android.systemui.statusbar.notification.stack.data.repository.headsUpNotificationRepository
 import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlin.test.Test
+import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
 import org.junit.runner.RunWith
@@ -48,7 +55,12 @@
 @RunWith(AndroidJUnit4::class)
 @EnableFlags(StatusBarNotifChips.FLAG_NAME)
 class NotifChipsViewModelTest : SysuiTestCase() {
-    private val kosmos = testKosmos().useUnconfinedTestDispatcher()
+    private val kosmos =
+        testKosmos().useUnconfinedTestDispatcher().apply {
+            // Don't be in lockscreen so that HUNs are allowed
+            fakeKeyguardTransitionRepository =
+                FakeKeyguardTransitionRepository(initInLockscreen = false, testScope = testScope)
+        }
     private val activeNotificationListRepository = kosmos.activeNotificationListRepository
 
     private val underTest by lazy { kosmos.notifChipsViewModel }
@@ -104,7 +116,7 @@
 
             assertThat(latest).hasSize(1)
             val chip = latest!![0]
-            assertThat(chip).isInstanceOf(OngoingActivityChipModel.Shown.ShortTimeDelta::class.java)
+            assertThat(chip).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
             assertThat(chip.icon).isEqualTo(OngoingActivityChipModel.ChipIcon.StatusBarView(icon))
         }
 
@@ -127,7 +139,7 @@
 
             assertThat(latest).hasSize(1)
             val chip = latest!![0]
-            assertThat(chip).isInstanceOf(OngoingActivityChipModel.Shown.ShortTimeDelta::class.java)
+            assertThat(chip).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
             assertThat(chip.icon)
                 .isEqualTo(OngoingActivityChipModel.ChipIcon.StatusBarNotificationIcon(notifKey))
         }
@@ -230,6 +242,206 @@
         }
 
     @Test
+    fun chips_hasShortCriticalText_usesTextInsteadOfTime() =
+        kosmos.runTest {
+            val latest by collectLastValue(underTest.chips)
+
+            val promotedContentBuilder =
+                PromotedNotificationContentModel.Builder("notif").apply {
+                    this.shortCriticalText = "Arrived"
+                    this.time =
+                        PromotedNotificationContentModel.When(
+                            time = 6543L,
+                            mode = PromotedNotificationContentModel.When.Mode.BasicTime,
+                        )
+                }
+            setNotifs(
+                listOf(
+                    activeNotificationModel(
+                        key = "notif",
+                        statusBarChipIcon = mock<StatusBarIconView>(),
+                        promotedContent = promotedContentBuilder.build(),
+                    )
+                )
+            )
+
+            assertThat(latest).hasSize(1)
+            assertThat(latest!![0]).isInstanceOf(OngoingActivityChipModel.Shown.Text::class.java)
+            assertThat((latest!![0] as OngoingActivityChipModel.Shown.Text).text)
+                .isEqualTo("Arrived")
+        }
+
+    @Test
+    fun chips_noTime_isIconOnly() =
+        kosmos.runTest {
+            val latest by collectLastValue(underTest.chips)
+
+            val promotedContentBuilder =
+                PromotedNotificationContentModel.Builder("notif").apply { this.time = null }
+            setNotifs(
+                listOf(
+                    activeNotificationModel(
+                        key = "notif",
+                        statusBarChipIcon = mock<StatusBarIconView>(),
+                        promotedContent = promotedContentBuilder.build(),
+                    )
+                )
+            )
+
+            assertThat(latest).hasSize(1)
+            assertThat(latest!![0])
+                .isInstanceOf(OngoingActivityChipModel.Shown.IconOnly::class.java)
+        }
+
+    @Test
+    fun chips_basicTime_isShortTimeDelta() =
+        kosmos.runTest {
+            val latest by collectLastValue(underTest.chips)
+
+            val promotedContentBuilder =
+                PromotedNotificationContentModel.Builder("notif").apply {
+                    this.time =
+                        PromotedNotificationContentModel.When(
+                            time = 6543L,
+                            mode = PromotedNotificationContentModel.When.Mode.BasicTime,
+                        )
+                }
+            setNotifs(
+                listOf(
+                    activeNotificationModel(
+                        key = "notif",
+                        statusBarChipIcon = mock<StatusBarIconView>(),
+                        promotedContent = promotedContentBuilder.build(),
+                    )
+                )
+            )
+
+            assertThat(latest).hasSize(1)
+            assertThat(latest!![0])
+                .isInstanceOf(OngoingActivityChipModel.Shown.ShortTimeDelta::class.java)
+        }
+
+    @Test
+    fun chips_countUpTime_isTimer() =
+        kosmos.runTest {
+            val latest by collectLastValue(underTest.chips)
+
+            val promotedContentBuilder =
+                PromotedNotificationContentModel.Builder("notif").apply {
+                    this.time =
+                        PromotedNotificationContentModel.When(
+                            time = 6543L,
+                            mode = PromotedNotificationContentModel.When.Mode.CountUp,
+                        )
+                }
+            setNotifs(
+                listOf(
+                    activeNotificationModel(
+                        key = "notif",
+                        statusBarChipIcon = mock<StatusBarIconView>(),
+                        promotedContent = promotedContentBuilder.build(),
+                    )
+                )
+            )
+
+            assertThat(latest).hasSize(1)
+            assertThat(latest!![0]).isInstanceOf(OngoingActivityChipModel.Shown.Timer::class.java)
+        }
+
+    @Test
+    fun chips_countDownTime_isTimer() =
+        kosmos.runTest {
+            val latest by collectLastValue(underTest.chips)
+
+            val promotedContentBuilder =
+                PromotedNotificationContentModel.Builder("notif").apply {
+                    this.time =
+                        PromotedNotificationContentModel.When(
+                            time = 6543L,
+                            mode = PromotedNotificationContentModel.When.Mode.CountDown,
+                        )
+                }
+            setNotifs(
+                listOf(
+                    activeNotificationModel(
+                        key = "notif",
+                        statusBarChipIcon = mock<StatusBarIconView>(),
+                        promotedContent = promotedContentBuilder.build(),
+                    )
+                )
+            )
+
+            assertThat(latest).hasSize(1)
+            assertThat(latest!![0]).isInstanceOf(OngoingActivityChipModel.Shown.Timer::class.java)
+        }
+
+    @Test
+    fun chips_noHeadsUp_showsTime() =
+        kosmos.runTest {
+            val latest by collectLastValue(underTest.chips)
+
+            val promotedContentBuilder =
+                PromotedNotificationContentModel.Builder("notif").apply {
+                    this.time =
+                        PromotedNotificationContentModel.When(
+                            time = 6543L,
+                            mode = PromotedNotificationContentModel.When.Mode.BasicTime,
+                        )
+                }
+            setNotifs(
+                listOf(
+                    activeNotificationModel(
+                        key = "notif",
+                        statusBarChipIcon = mock<StatusBarIconView>(),
+                        promotedContent = promotedContentBuilder.build(),
+                    )
+                )
+            )
+
+            // WHEN there's no HUN
+            kosmos.headsUpNotificationRepository.setNotifications(emptyList())
+
+            // THEN the chip shows the time
+            assertThat(latest!![0])
+                .isInstanceOf(OngoingActivityChipModel.Shown.ShortTimeDelta::class.java)
+        }
+
+    @Test
+    fun chips_hasHeadsUpByUser_onlyShowsIcon() =
+        kosmos.runTest {
+            val latest by collectLastValue(underTest.chips)
+
+            val promotedContentBuilder =
+                PromotedNotificationContentModel.Builder("notif").apply {
+                    this.time =
+                        PromotedNotificationContentModel.When(
+                            time = 6543L,
+                            mode = PromotedNotificationContentModel.When.Mode.BasicTime,
+                        )
+                }
+            setNotifs(
+                listOf(
+                    activeNotificationModel(
+                        key = "notif",
+                        statusBarChipIcon = mock<StatusBarIconView>(),
+                        promotedContent = promotedContentBuilder.build(),
+                    )
+                )
+            )
+
+            // WHEN there's a HUN pinned by a user
+            kosmos.headsUpNotificationRepository.setNotifications(
+                UnconfinedFakeHeadsUpRowRepository(
+                    key = "notif",
+                    pinnedStatus = MutableStateFlow(PinnedStatus.PinnedByUser),
+                )
+            )
+
+            assertThat(latest!![0])
+                .isInstanceOf(OngoingActivityChipModel.Shown.IconOnly::class.java)
+        }
+
+    @Test
     fun chips_clickingChipNotifiesInteractor() =
         kosmos.runTest {
             val latest by collectLastValue(underTest.chips)
@@ -264,15 +476,11 @@
 
     companion object {
         fun assertIsNotifChip(latest: OngoingActivityChipModel?, expectedIcon: StatusBarIconView) {
-            assertThat(latest)
-                .isInstanceOf(OngoingActivityChipModel.Shown.ShortTimeDelta::class.java)
             assertThat((latest as OngoingActivityChipModel.Shown).icon)
                 .isEqualTo(OngoingActivityChipModel.ChipIcon.StatusBarView(expectedIcon))
         }
 
         fun assertIsNotifKey(latest: OngoingActivityChipModel?, expectedKey: String) {
-            assertThat(latest)
-                .isInstanceOf(OngoingActivityChipModel.Shown.ShortTimeDelta::class.java)
             assertThat((latest as OngoingActivityChipModel.Shown).icon)
                 .isEqualTo(OngoingActivityChipModel.ChipIcon.StatusBarNotificationIcon(expectedKey))
         }
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 1d7f257..a3ffd91 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
@@ -50,7 +50,6 @@
 import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderWrapper.DecisionImpl
 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.phone.NotificationGroupTestHelper
 import com.android.systemui.testKosmos
 import com.android.systemui.util.concurrency.FakeExecutor
@@ -76,6 +75,7 @@
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.`when` as whenever
 import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.argumentCaptor
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
@@ -315,7 +315,7 @@
 
         // WHEN the notification is added but not yet binding
         collectionListener.onEntryAdded(entry)
-        verify(headsUpViewBinder, never()).bindHeadsUpView(eq(entry), any())
+        verify(headsUpViewBinder, never()).bindHeadsUpView(eq(entry), any(), any())
 
         // THEN only promote mEntry
         assertTrue(notifPromoter.shouldPromoteToTopLevel(entry))
@@ -330,7 +330,7 @@
         collectionListener.onEntryAdded(entry)
         beforeTransformGroupsListener.onBeforeTransformGroups(listOf(entry))
         beforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(entry))
-        verify(headsUpViewBinder).bindHeadsUpView(eq(entry), any())
+        verify(headsUpViewBinder).bindHeadsUpView(eq(entry), eq(false), any())
 
         // THEN only promote mEntry
         assertTrue(notifPromoter.shouldPromoteToTopLevel(entry))
@@ -382,11 +382,8 @@
         collectionListener.onEntryAdded(entry)
         beforeTransformGroupsListener.onBeforeTransformGroups(listOf(entry))
         beforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(entry))
-        verify(headsUpManager, never()).showNotification(entry)
-        withArgCaptor<BindCallback> {
-                verify(headsUpViewBinder).bindHeadsUpView(eq(entry), capture())
-            }
-            .onBindFinished(entry)
+
+        finishBind(entry)
 
         // THEN we tell the HeadsUpManager to show the notification
         verify(headsUpManager).showNotification(entry)
@@ -401,8 +398,8 @@
         beforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(entry))
 
         // THEN we never bind the heads up view or tell HeadsUpManager to show the notification
-        verify(headsUpViewBinder, never()).bindHeadsUpView(eq(entry), any())
-        verify(headsUpManager, never()).showNotification(entry)
+        verify(headsUpViewBinder, never()).bindHeadsUpView(eq(entry), any(), any())
+        verify(headsUpManager, never()).showNotification(eq(entry), any())
     }
 
     @Test
@@ -435,8 +432,8 @@
         beforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(entry))
 
         // THEN the notification is never bound or shown
-        verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any())
-        verify(headsUpManager, never()).showNotification(any())
+        verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any(), any())
+        verify(headsUpManager, never()).showNotification(any(), any())
     }
 
     @Test
@@ -471,7 +468,7 @@
             beforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(entry))
 
             finishBind(entry)
-            verify(headsUpManager).showNotification(entry)
+            verify(headsUpManager).showNotification(entry, isPinnedByUser = true)
         }
 
     @Test
@@ -485,8 +482,8 @@
             executor.runAllReady()
             beforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(entry))
 
-            verify(headsUpViewBinder, never()).bindHeadsUpView(eq(entry), any())
-            verify(headsUpManager, never()).showNotification(entry)
+            verify(headsUpViewBinder, never()).bindHeadsUpView(eq(entry), any(), any())
+            verify(headsUpManager, never()).showNotification(eq(entry), any())
         }
 
     @Test
@@ -509,7 +506,7 @@
 
             // THEN it's still shown as heads up
             finishBind(entry)
-            verify(headsUpManager).showNotification(entry)
+            verify(headsUpManager).showNotification(entry, isPinnedByUser = true)
         }
 
     @Test
@@ -525,8 +522,8 @@
             beforeTransformGroupsListener.onBeforeTransformGroups(listOf(promotedEntry))
             beforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(promotedEntry))
 
-            verify(headsUpViewBinder, never()).bindHeadsUpView(eq(promotedEntry), any())
-            verify(headsUpManager, never()).showNotification(promotedEntry)
+            verify(headsUpViewBinder, never()).bindHeadsUpView(eq(promotedEntry), any(), any())
+            verify(headsUpManager, never()).showNotification(eq(promotedEntry), any())
 
             // Then a new notification comes in that should be heads up
             setShouldHeadsUp(entry, false)
@@ -544,10 +541,10 @@
 
             // THEN the promoted entry is shown as a HUN, *not* the new entry
             finishBind(promotedEntry)
-            verify(headsUpManager).showNotification(promotedEntry)
+            verify(headsUpManager).showNotification(promotedEntry, isPinnedByUser = true)
 
-            verify(headsUpViewBinder, never()).bindHeadsUpView(eq(entry), any())
-            verify(headsUpManager, never()).showNotification(entry)
+            verify(headsUpViewBinder, never()).bindHeadsUpView(eq(entry), any(), any())
+            verify(headsUpManager, never()).showNotification(eq(entry), any())
         }
 
     @Test
@@ -558,10 +555,10 @@
         collectionListener.onEntryAdded(groupSummary)
         collectionListener.onEntryAdded(groupSibling1)
         beforeTransformGroupsListener.onBeforeTransformGroups(listOf(groupSibling1))
-        verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any())
+        verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any(), any())
         beforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(groupSibling1))
 
-        verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSummary), any())
+        verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSummary), any(), any())
         finishBind(groupSibling1)
 
         verify(headsUpManager, never()).showNotification(groupSummary)
@@ -580,10 +577,10 @@
         collectionListener.onEntryAdded(groupSummary)
         collectionListener.onEntryAdded(groupChild1)
         beforeTransformGroupsListener.onBeforeTransformGroups(listOf(groupChild1))
-        verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any())
+        verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any(), any())
         beforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(groupChild1))
 
-        verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSummary), any())
+        verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSummary), any(), any())
         finishBind(groupChild1)
 
         verify(headsUpManager, never()).showNotification(groupSummary)
@@ -603,12 +600,12 @@
         collectionListener.onEntryAdded(groupSibling2)
         val entryList = listOf(groupSibling1, groupSibling2)
         beforeTransformGroupsListener.onBeforeTransformGroups(entryList)
-        verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any())
+        verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any(), any())
         beforeFinalizeFilterListener.onBeforeFinalizeFilter(entryList)
 
-        verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSummary), any())
+        verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSummary), any(), any())
         finishBind(groupSibling1)
-        verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSibling2), any())
+        verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSibling2), any(), any())
 
         // THEN we tell the HeadsUpManager to show the notification
         verify(headsUpManager, never()).showNotification(groupSummary)
@@ -629,12 +626,12 @@
         collectionListener.onEntryAdded(groupChild2)
         val entryList = listOf(groupChild1, groupChild2)
         beforeTransformGroupsListener.onBeforeTransformGroups(entryList)
-        verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any())
+        verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any(), any())
         beforeFinalizeFilterListener.onBeforeFinalizeFilter(entryList)
 
-        verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSummary), any())
+        verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSummary), any(), any())
         finishBind(groupChild1)
-        verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupChild2), any())
+        verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupChild2), any(), any())
 
         // THEN we tell the HeadsUpManager to show the notification
         verify(headsUpManager, never()).showNotification(groupSummary)
@@ -661,7 +658,7 @@
                 .setChildren(listOf(groupSibling1, groupPriority, groupSibling2))
                 .build()
         beforeTransformGroupsListener.onBeforeTransformGroups(listOf(beforeTransformGroup))
-        verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any())
+        verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any(), any())
 
         val afterTransformGroup =
             GroupEntryBuilder()
@@ -672,10 +669,10 @@
             listOf(groupPriority, afterTransformGroup)
         )
 
-        verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSummary), any())
+        verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSummary), any(), any())
         finishBind(groupPriority)
-        verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSibling1), any())
-        verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSibling2), any())
+        verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSibling1), any(), any())
+        verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSibling2), any(), any())
 
         // THEN we tell the HeadsUpManager to show the notification
         verify(headsUpManager, never()).showNotification(groupSummary)
@@ -702,7 +699,7 @@
                 .setChildren(listOf(groupSibling1, groupPriority, groupSibling2))
                 .build()
         beforeTransformGroupsListener.onBeforeTransformGroups(listOf(beforeTransformGroup))
-        verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any())
+        verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any(), any())
 
         val afterTransformGroup =
             GroupEntryBuilder()
@@ -713,10 +710,10 @@
             listOf(groupPriority, afterTransformGroup)
         )
 
-        verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSummary), any())
+        verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSummary), any(), any())
         finishBind(groupPriority)
-        verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSibling1), any())
-        verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSibling2), any())
+        verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSibling1), any(), any())
+        verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSibling2), any(), any())
 
         verify(headsUpManager, never()).showNotification(groupSummary)
         verify(headsUpManager).showNotification(groupPriority)
@@ -740,7 +737,7 @@
                 .setChildren(listOf(groupSibling1, groupPriority, groupSibling2))
                 .build()
         beforeTransformGroupsListener.onBeforeTransformGroups(listOf(beforeTransformGroup))
-        verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any())
+        verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any(), any())
 
         val afterTransformGroup =
             GroupEntryBuilder()
@@ -751,10 +748,10 @@
             listOf(groupPriority, afterTransformGroup)
         )
 
-        verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSummary), any())
+        verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSummary), any(), any())
         finishBind(groupPriority)
-        verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSibling1), any())
-        verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSibling2), any())
+        verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSibling1), any(), any())
+        verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSibling2), any(), any())
 
         verify(headsUpManager, never()).showNotification(groupSummary)
         verify(headsUpManager).showNotification(groupPriority)
@@ -779,7 +776,7 @@
                 .setChildren(listOf(groupSibling1, groupPriority, groupSibling2))
                 .build()
         beforeTransformGroupsListener.onBeforeTransformGroups(listOf(beforeTransformGroup))
-        verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any())
+        verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any(), any())
 
         val afterTransformGroup =
             GroupEntryBuilder()
@@ -791,9 +788,9 @@
         )
 
         finishBind(groupSummary)
-        verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupPriority), any())
-        verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSibling1), any())
-        verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSibling2), any())
+        verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupPriority), any(), any())
+        verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSibling1), any(), any())
+        verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSibling2), any(), any())
 
         verify(headsUpManager).showNotification(groupSummary)
         verify(headsUpManager, never()).showNotification(groupPriority)
@@ -816,12 +813,12 @@
                 .setChildren(listOf(groupSibling1, groupSibling2))
                 .build()
         beforeTransformGroupsListener.onBeforeTransformGroups(listOf(groupEntry))
-        verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any())
+        verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any(), any())
         beforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(groupEntry))
 
         finishBind(groupSummary)
-        verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSibling1), any())
-        verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSibling2), any())
+        verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSibling1), any(), any())
+        verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSibling2), any(), any())
 
         verify(headsUpManager).showNotification(groupSummary)
         verify(headsUpManager, never()).showNotification(groupSibling1)
@@ -842,12 +839,12 @@
                 .setChildren(listOf(groupChild1, groupChild2))
                 .build()
         beforeTransformGroupsListener.onBeforeTransformGroups(listOf(groupEntry))
-        verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any())
+        verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any(), any())
         beforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(groupEntry))
 
         finishBind(groupSummary)
-        verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupChild1), any())
-        verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupChild2), any())
+        verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupChild1), any(), any())
+        verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupChild2), any(), any())
 
         verify(headsUpManager).showNotification(groupSummary)
         verify(headsUpManager, never()).showNotification(groupChild1)
@@ -871,12 +868,12 @@
                 .setChildren(listOf(groupChild1, groupChild2))
                 .build()
         beforeTransformGroupsListener.onBeforeTransformGroups(listOf(groupEntry))
-        verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any())
+        verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any(), any())
         beforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(groupEntry))
 
-        verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSummary), any())
+        verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSummary), any(), any())
         finishBind(groupChild1)
-        verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupChild2), any())
+        verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupChild2), any(), any())
 
         verify(headsUpManager, never()).showNotification(groupSummary)
         verify(headsUpManager).showNotification(groupChild1)
@@ -900,7 +897,7 @@
 
         // THEN the notification is shown
         finishBind(entry)
-        verify(headsUpManager).showNotification(entry)
+        verify(headsUpManager).showNotification(entry, isPinnedByUser = false)
     }
 
     @Test
@@ -917,8 +914,8 @@
         beforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(entry))
 
         // THEN the notification is never bound or shown
-        verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any())
-        verify(headsUpManager, never()).showNotification(any())
+        verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any(), any())
+        verify(headsUpManager, never()).showNotification(any(), any())
     }
 
     @Test
@@ -938,7 +935,7 @@
 
         // THEN the notification is shown
         finishBind(entry)
-        verify(headsUpManager).showNotification(entry)
+        verify(headsUpManager).showNotification(entry, isPinnedByUser = false)
     }
 
     @Test
@@ -958,8 +955,8 @@
         beforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(entry))
 
         // THEN the notification is never bound or shown
-        verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any())
-        verify(headsUpManager, never()).showNotification(any())
+        verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any(), any())
+        verify(headsUpManager, never()).showNotification(any(), any())
     }
 
     @Test
@@ -1022,8 +1019,8 @@
 
         // THEN it should full screen and log but it should NOT HUN
         verify(launchFullScreenIntentProvider).launchFullScreenIntent(entry)
-        verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any())
-        verify(headsUpManager, never()).showNotification(any())
+        verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any(), any())
+        verify(headsUpManager, never()).showNotification(any(), any())
         verifyLoggedFullScreenIntentDecision(
             entry,
             FullScreenIntentDecision.FSI_DEVICE_NOT_INTERACTIVE,
@@ -1063,8 +1060,8 @@
 
         // THEN it should still not yet full screen or HUN
         verify(launchFullScreenIntentProvider, never()).launchFullScreenIntent(any())
-        verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any())
-        verify(headsUpManager, never()).showNotification(any())
+        verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any(), any())
+        verify(headsUpManager, never()).showNotification(any(), any())
 
         // Same decision as before; is not logged
         verifyNoFullScreenIntentDecisionLogged()
@@ -1080,8 +1077,8 @@
 
         // THEN it should full screen and log but it should NOT HUN
         verify(launchFullScreenIntentProvider).launchFullScreenIntent(entry)
-        verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any())
-        verify(headsUpManager, never()).showNotification(any())
+        verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any(), any())
+        verify(headsUpManager, never()).showNotification(any(), any())
         verifyLoggedFullScreenIntentDecision(
             entry,
             FullScreenIntentDecision.FSI_DEVICE_NOT_INTERACTIVE,
@@ -1107,8 +1104,8 @@
 
         // THEN it should NOT full screen or HUN
         verify(launchFullScreenIntentProvider, never()).launchFullScreenIntent(any())
-        verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any())
-        verify(headsUpManager, never()).showNotification(any())
+        verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any(), any())
+        verify(headsUpManager, never()).showNotification(any(), any())
 
         // NOW the DND logic changes and FSI and HUN are available
         clearInvocations(launchFullScreenIntentProvider)
@@ -1121,7 +1118,7 @@
         // VERIFY that the FSI didn't happen, but that we do HUN
         verify(launchFullScreenIntentProvider, never()).launchFullScreenIntent(any())
         finishBind(entry)
-        verify(headsUpManager).showNotification(entry)
+        verify(headsUpManager).showNotification(entry, isPinnedByUser = false)
     }
 
     @Test
@@ -1206,10 +1203,12 @@
     }
 
     private fun finishBind(entry: NotificationEntry) {
-        verify(headsUpManager, never()).showNotification(entry)
-        withArgCaptor<BindCallback> {
-                verify(headsUpViewBinder).bindHeadsUpView(eq(entry), capture())
+        verify(headsUpManager, never()).showNotification(eq(entry), any())
+        val isPinnedByUserCaptor = argumentCaptor<Boolean>()
+        withArgCaptor<HeadsUpViewBinder.HeadsUpBindCallback> {
+                verify(headsUpViewBinder)
+                    .bindHeadsUpView(eq(entry), isPinnedByUserCaptor.capture(), capture())
             }
-            .onBindFinished(entry)
+            .onHeadsUpBindFinished(entry, isPinnedByUserCaptor.firstValue)
     }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinatorTest.kt
index 9e7befd..8f21ddff 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinatorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinatorTest.kt
@@ -50,6 +50,8 @@
 import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.statusbar.NotificationLockscreenUserManager
+import com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTION_TYPE_NONE
+import com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTION_TYPE_PUBLIC
 import com.android.systemui.statusbar.RankingBuilder
 import com.android.systemui.statusbar.StatusBarState
 import com.android.systemui.statusbar.SysuiStatusBarStateController
@@ -840,7 +842,13 @@
                 whenever(sbn).thenReturn(mockSbn)
                 whenever(row).thenReturn(mockRow)
             }
-        whenever(lockscreenUserManager.needsRedaction(mockEntry)).thenReturn(needsRedaction)
+        val redactionType =
+            if (needsRedaction) {
+                REDACTION_TYPE_PUBLIC
+            } else {
+                REDACTION_TYPE_NONE
+            }
+        whenever(lockscreenUserManager.getRedactionType(mockEntry)).thenReturn(redactionType)
         whenever(mockEntry.rowExists()).thenReturn(true)
         return object : ListEntry("key", 0) {
             override fun getRepresentativeEntry(): NotificationEntry = mockEntry
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProviderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProviderTest.kt
index 2f77b33..3c772fd 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProviderTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProviderTest.kt
@@ -27,6 +27,8 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.settings.UserTracker
 import com.android.systemui.statusbar.NotificationLockscreenUserManager
+import com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTION_TYPE_NONE
+import com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTION_TYPE_PUBLIC
 import com.android.systemui.statusbar.notification.collection.GroupEntry
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
 import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection
@@ -42,6 +44,7 @@
 import com.android.systemui.util.settings.FakeSettings
 import com.android.systemui.util.settings.SecureSettings
 import com.google.common.truth.Truth.assertThat
+import kotlin.test.assertEquals
 import kotlin.test.assertFalse
 import kotlin.test.assertTrue
 import org.junit.Before
@@ -212,12 +215,12 @@
         whenever(sensitiveNotifProtectionController.shouldProtectNotification(entry))
             .thenReturn(false)
         val oldAdjustment: NotifUiAdjustment = adjustmentProvider.calculateAdjustment(entry)
-        assertFalse(oldAdjustment.needsRedaction)
+        assertEquals(REDACTION_TYPE_NONE, oldAdjustment.redactionType)
 
         whenever(sensitiveNotifProtectionController.shouldProtectNotification(entry))
             .thenReturn(true)
         val newAdjustment = adjustmentProvider.calculateAdjustment(entry)
-        assertTrue(newAdjustment.needsRedaction)
+        assertEquals(REDACTION_TYPE_PUBLIC, newAdjustment.redactionType)
 
         // Then: need re-inflation
         assertTrue(NotifUiAdjustment.needReinflate(oldAdjustment, newAdjustment))
@@ -229,12 +232,12 @@
         whenever(sensitiveNotifProtectionController.shouldProtectNotification(entry))
             .thenReturn(false)
         val oldAdjustment = adjustmentProvider.calculateAdjustment(entry)
-        assertFalse(oldAdjustment.needsRedaction)
+        assertEquals(REDACTION_TYPE_NONE, oldAdjustment.redactionType)
 
         whenever(sensitiveNotifProtectionController.shouldProtectNotification(entry))
             .thenReturn(true)
         val newAdjustment = adjustmentProvider.calculateAdjustment(entry)
-        assertFalse(newAdjustment.needsRedaction)
+        assertEquals(REDACTION_TYPE_NONE, newAdjustment.redactionType)
 
         // Then: need no re-inflation
         assertFalse(NotifUiAdjustment.needReinflate(oldAdjustment, newAdjustment))
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractorTest.kt
index dc0231f..22ef408 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractorTest.kt
@@ -28,6 +28,7 @@
 import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.kosmos.testScope
+import com.android.systemui.shade.data.repository.fakeShadeRepository
 import com.android.systemui.shade.shadeTestUtil
 import com.android.systemui.statusbar.notification.data.repository.FakeHeadsUpRowRepository
 import com.android.systemui.statusbar.notification.data.repository.notificationsKeyguardViewStateRepository
@@ -409,76 +410,101 @@
         }
 
     @Test
-    fun showHeadsUpStatusBar_true() =
+    fun statusBarHeadsUpState_pinnedBySystem() =
         testScope.runTest {
-            val showHeadsUpStatusBar by collectLastValue(underTest.showHeadsUpStatusBar)
+            val statusBarHeadsUpState by collectLastValue(underTest.statusBarHeadsUpState)
 
-            // WHEN a row is pinned
-            headsUpRepository.setNotifications(fakeHeadsUpRowRepository("key 0", isPinned = true))
+            headsUpRepository.setNotifications(
+                FakeHeadsUpRowRepository(key = "key 0", pinnedStatus = PinnedStatus.PinnedBySystem)
+            )
+            runCurrent()
 
-            assertThat(showHeadsUpStatusBar).isTrue()
+            assertThat(statusBarHeadsUpState).isEqualTo(PinnedStatus.PinnedBySystem)
         }
 
     @Test
-    fun showHeadsUpStatusBar_withoutPinnedNotifications_false() =
+    fun statusBarHeadsUpState_pinnedByUser() =
         testScope.runTest {
-            val showHeadsUpStatusBar by collectLastValue(underTest.showHeadsUpStatusBar)
+            val statusBarHeadsUpState by collectLastValue(underTest.statusBarHeadsUpState)
 
-            // WHEN no row is pinned
-            headsUpRepository.setNotifications(fakeHeadsUpRowRepository("key 0", isPinned = false))
+            headsUpRepository.setNotifications(
+                FakeHeadsUpRowRepository(key = "key 0", pinnedStatus = PinnedStatus.PinnedByUser)
+            )
+            runCurrent()
 
-            assertThat(showHeadsUpStatusBar).isFalse()
+            assertThat(statusBarHeadsUpState).isEqualTo(PinnedStatus.PinnedByUser)
         }
 
     @Test
-    fun showHeadsUpStatusBar_whenShadeExpanded_false() =
+    fun statusBarHeadsUpState_withoutPinnedNotifications_notPinned() =
         testScope.runTest {
-            val showHeadsUpStatusBar by collectLastValue(underTest.showHeadsUpStatusBar)
+            val statusBarHeadsUpState by collectLastValue(underTest.statusBarHeadsUpState)
+
+            headsUpRepository.setNotifications(
+                FakeHeadsUpRowRepository(key = "key 0", PinnedStatus.NotPinned)
+            )
+            runCurrent()
+
+            assertThat(statusBarHeadsUpState).isEqualTo(PinnedStatus.NotPinned)
+        }
+
+    @Test
+    fun statusBarHeadsUpState_whenShadeExpanded_false() =
+        testScope.runTest {
+            val statusBarHeadsUpState by collectLastValue(underTest.statusBarHeadsUpState)
 
             // WHEN a row is pinned
             headsUpRepository.setNotifications(fakeHeadsUpRowRepository("key 0", isPinned = true))
+            runCurrent()
             // AND the shade is expanded
             shadeTestUtil.setShadeExpansion(1.0f)
+            // Needed if SceneContainer flag is off: `ShadeTestUtil.setShadeExpansion(1f)`
+            // incorrectly causes `ShadeInteractor.isShadeFullyCollapsed` to emit `true`, when it
+            // should emit `false`.
+            kosmos.fakeShadeRepository.setLegacyShadeExpansion(1.0f)
 
-            assertThat(showHeadsUpStatusBar).isFalse()
+            assertThat(statusBarHeadsUpState!!.isPinned).isFalse()
         }
 
     @Test
-    fun showHeadsUpStatusBar_notificationsAreHidden_false() =
+    fun statusBarHeadsUpState_notificationsAreHidden_false() =
         testScope.runTest {
-            val showHeadsUpStatusBar by collectLastValue(underTest.showHeadsUpStatusBar)
+            val statusBarHeadsUpState by collectLastValue(underTest.statusBarHeadsUpState)
 
             // WHEN a row is pinned
             headsUpRepository.setNotifications(fakeHeadsUpRowRepository("key 0", isPinned = true))
+            runCurrent()
             // AND the notifications are hidden
             keyguardViewStateRepository.areNotificationsFullyHidden.value = true
 
-            assertThat(showHeadsUpStatusBar).isFalse()
+            assertThat(statusBarHeadsUpState!!.isPinned).isFalse()
         }
 
     @Test
-    fun showHeadsUpStatusBar_onLockScreen_false() =
+    fun statusBarHeadsUpState_onLockScreen_false() =
         testScope.runTest {
-            val showHeadsUpStatusBar by collectLastValue(underTest.showHeadsUpStatusBar)
+            val statusBarHeadsUpState by collectLastValue(underTest.statusBarHeadsUpState)
 
             // WHEN a row is pinned
             headsUpRepository.setNotifications(fakeHeadsUpRowRepository("key 0", isPinned = true))
+            runCurrent()
             // AND the lock screen is shown
             keyguardTransitionRepository.emitInitialStepsFromOff(
                 to = KeyguardState.LOCKSCREEN,
                 testSetup = true,
             )
 
-            assertThat(showHeadsUpStatusBar).isFalse()
+            assertThat(statusBarHeadsUpState!!.isPinned).isFalse()
         }
 
     @Test
-    fun showHeadsUpStatusBar_onByPassLockScreen_true() =
+    fun statusBarHeadsUpState_onByPassLockScreen_true() =
         testScope.runTest {
-            val showHeadsUpStatusBar by collectLastValue(underTest.showHeadsUpStatusBar)
+            val statusBarHeadsUpState by collectLastValue(underTest.statusBarHeadsUpState)
 
             // WHEN a row is pinned
             headsUpRepository.setNotifications(fakeHeadsUpRowRepository("key 0", isPinned = true))
+            runCurrent()
             // AND the lock screen is shown
             keyguardTransitionRepository.emitInitialStepsFromOff(
                 to = KeyguardState.LOCKSCREEN,
@@ -487,13 +513,13 @@
             // AND bypass is enabled
             faceAuthRepository.isBypassEnabled.value = true
 
-            assertThat(showHeadsUpStatusBar).isTrue()
+            assertThat(statusBarHeadsUpState!!.isPinned).isTrue()
         }
 
     @Test
-    fun showHeadsUpStatusBar_onByPassLockScreen_withoutNotifications_false() =
+    fun statusBarHeadsUpState_onByPassLockScreen_withoutNotifications_false() =
         testScope.runTest {
-            val showHeadsUpStatusBar by collectLastValue(underTest.showHeadsUpStatusBar)
+            val statusBarHeadsUpState by collectLastValue(underTest.statusBarHeadsUpState)
 
             // WHEN no pinned rows
             // AND the lock screen is shown
@@ -504,7 +530,7 @@
             // AND bypass is enabled
             faceAuthRepository.isBypassEnabled.value = true
 
-            assertThat(showHeadsUpStatusBar).isFalse()
+            assertThat(statusBarHeadsUpState!!.isPinned).isFalse()
         }
 
     private fun fakeHeadsUpRowRepository(key: String, isPinned: Boolean = false) =
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImplTest.kt
index 8420c49..739a9c9 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImplTest.kt
@@ -40,11 +40,11 @@
 import com.android.systemui.shade.domain.interactor.shadeInteractor
 import com.android.systemui.shade.shadeTestUtil
 import com.android.systemui.statusbar.StatusBarState
+import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
 import com.android.systemui.statusbar.notification.collection.provider.visualStabilityProvider
 import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager
-import com.android.systemui.statusbar.notification.headsup.HeadsUpManagerImpl.HeadsUpEntry
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
 import com.android.systemui.statusbar.notification.row.NotificationTestHelper
 import com.android.systemui.statusbar.notification.shared.NotificationThrottleHun
@@ -179,6 +179,44 @@
     }
 
     @Test
+    fun pinnedHeadsUpStatuses_noHeadsUp() {
+        assertThat(underTest.hasPinnedHeadsUp()).isFalse()
+        assertThat(underTest.pinnedHeadsUpStatus()).isEqualTo(PinnedStatus.NotPinned)
+    }
+
+    @Test
+    fun pinnedHeadsUpStatuses_pinnedBySystem() {
+        val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+        entry.row = testHelper.createRow()
+        underTest.showNotification(entry, isPinnedByUser = false)
+
+        assertThat(underTest.hasPinnedHeadsUp()).isTrue()
+        assertThat(underTest.pinnedHeadsUpStatus()).isEqualTo(PinnedStatus.PinnedBySystem)
+    }
+
+    @Test
+    @DisableFlags(StatusBarNotifChips.FLAG_NAME)
+    fun pinnedHeadsUpStatuses_pinnedByUser_butFlagOff_returnsNotPinned() {
+        val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+        entry.row = testHelper.createRow()
+        underTest.showNotification(entry, isPinnedByUser = true)
+
+        assertThat(underTest.hasPinnedHeadsUp()).isFalse()
+        assertThat(underTest.pinnedHeadsUpStatus()).isEqualTo(PinnedStatus.NotPinned)
+    }
+
+    @Test
+    @EnableFlags(StatusBarNotifChips.FLAG_NAME)
+    fun pinnedHeadsUpStatuses_pinnedByUser_flagOn() {
+        val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+        entry.row = testHelper.createRow()
+        underTest.showNotification(entry, isPinnedByUser = true)
+
+        assertThat(underTest.hasPinnedHeadsUp()).isTrue()
+        assertThat(underTest.pinnedHeadsUpStatus()).isEqualTo(PinnedStatus.PinnedByUser)
+    }
+
+    @Test
     @EnableFlags(NotificationThrottleHun.FLAG_NAME)
     fun testGetHeadsUpEntryList_includesAvalancheEntryList() {
         val notifEntry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
@@ -199,10 +237,10 @@
     }
 
     @Test
-    fun testShowNotification_addsEntry() {
+    fun testShowNotification_notPinnedByUser_addsEntry() {
         val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
 
-        underTest.showNotification(entry)
+        underTest.showNotification(entry, isPinnedByUser = false)
 
         assertThat(underTest.isHeadsUpEntry(entry.key)).isTrue()
         assertThat(underTest.hasNotifications()).isTrue()
@@ -210,20 +248,43 @@
     }
 
     @Test
-    fun testShowNotification_autoDismisses() {
+    @EnableFlags(StatusBarNotifChips.FLAG_NAME)
+    fun testShowNotification_isPinnedByUser_addsEntry() {
         val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
 
-        underTest.showNotification(entry)
+        underTest.showNotification(entry, isPinnedByUser = true)
+
+        assertThat(underTest.isHeadsUpEntry(entry.key)).isTrue()
+        assertThat(underTest.hasNotifications()).isTrue()
+        assertThat(underTest.getEntry(entry.key)).isEqualTo(entry)
+    }
+
+    @Test
+    fun testShowNotification_notPinnedByUser_autoDismisses() {
+        val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+
+        underTest.showNotification(entry, isPinnedByUser = false)
         systemClock.advanceTime((TEST_AUTO_DISMISS_TIME * 3 / 2).toLong())
 
         assertThat(underTest.isHeadsUpEntry(entry.key)).isFalse()
     }
 
     @Test
-    fun testRemoveNotification_removeDeferred() {
+    @EnableFlags(StatusBarNotifChips.FLAG_NAME)
+    fun testShowNotification_isPinnedByUser_autoDismisses() {
         val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
 
-        underTest.showNotification(entry)
+        underTest.showNotification(entry, isPinnedByUser = true)
+        systemClock.advanceTime((TEST_AUTO_DISMISS_TIME * 3 / 2).toLong())
+
+        assertThat(underTest.isHeadsUpEntry(entry.key)).isFalse()
+    }
+
+    @Test
+    fun testRemoveNotification_notPinnedByUser_removeDeferred() {
+        val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+
+        underTest.showNotification(entry, isPinnedByUser = false)
 
         val removedImmediately =
             underTest.removeNotification(
@@ -236,10 +297,27 @@
     }
 
     @Test
-    fun testRemoveNotification_forceRemove() {
+    @EnableFlags(StatusBarNotifChips.FLAG_NAME)
+    fun testRemoveNotification_isPinnedByUser_removeDeferred() {
         val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
 
-        underTest.showNotification(entry)
+        underTest.showNotification(entry, isPinnedByUser = true)
+
+        val removedImmediately =
+            underTest.removeNotification(
+                entry.key,
+                /* releaseImmediately= */ false,
+                "removeDeferred",
+            )
+        assertThat(removedImmediately).isFalse()
+        assertThat(underTest.isHeadsUpEntry(entry.key)).isTrue()
+    }
+
+    @Test
+    fun testRemoveNotification_notPinnedByUser_forceRemove() {
+        val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+
+        underTest.showNotification(entry, isPinnedByUser = false)
 
         val removedImmediately =
             underTest.removeNotification(entry.key, /* releaseImmediately= */ true, "forceRemove")
@@ -248,11 +326,26 @@
     }
 
     @Test
+    @EnableFlags(StatusBarNotifChips.FLAG_NAME)
+    fun testRemoveNotification_isPinnedByUser_forceRemove() {
+        val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+
+        underTest.showNotification(entry, isPinnedByUser = true)
+
+        val removedImmediately =
+            underTest.removeNotification(entry.key, /* releaseImmediately= */ true, "forceRemove")
+        assertThat(removedImmediately).isTrue()
+        assertThat(underTest.isHeadsUpEntry(entry.key)).isFalse()
+    }
+
+    @Test
+    @EnableFlags(StatusBarNotifChips.FLAG_NAME)
     fun testReleaseAllImmediately() {
         for (i in 0 until 4) {
             val entry = HeadsUpManagerTestUtil.createEntry(i, mContext)
-            entry.row = mock<ExpandableNotificationRow>()
-            underTest.showNotification(entry)
+            entry.row = testHelper.createRow()
+            val isPinnedByUser = i % 2 == 0
+            underTest.showNotification(entry, isPinnedByUser)
         }
 
         underTest.releaseAllImmediately()
@@ -261,10 +354,21 @@
     }
 
     @Test
-    fun testCanRemoveImmediately_notShownLongEnough() {
+    fun testCanRemoveImmediately_notShownLongEnough_notPinnedByUser() {
         val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
 
-        underTest.showNotification(entry)
+        underTest.showNotification(entry, isPinnedByUser = false)
+
+        // The entry has just been added so we should not remove immediately.
+        assertThat(underTest.canRemoveImmediately(entry.key)).isFalse()
+    }
+
+    @Test
+    @EnableFlags(StatusBarNotifChips.FLAG_NAME)
+    fun testCanRemoveImmediately_notShownLongEnough_isPinnedByUser() {
+        val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+
+        underTest.showNotification(entry, isPinnedByUser = true)
 
         // The entry has just been added so we should not remove immediately.
         assertThat(underTest.canRemoveImmediately(entry.key)).isFalse()
@@ -365,17 +469,48 @@
     }
 
     @Test
-    fun testSnooze() {
+    fun testSnooze_notPinnedByUser() {
         val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
-        underTest.showNotification(entry)
+        underTest.showNotification(entry, isPinnedByUser = false)
+
         underTest.snooze()
+
         assertThat(underTest.isSnoozed(entry.sbn.packageName)).isTrue()
     }
 
     @Test
-    fun testSwipedOutNotification() {
+    @EnableFlags(StatusBarNotifChips.FLAG_NAME)
+    fun testSnooze_isPinnedByUser() {
         val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
-        underTest.showNotification(entry)
+        underTest.showNotification(entry, isPinnedByUser = true)
+
+        underTest.snooze()
+
+        assertThat(underTest.isSnoozed(entry.sbn.packageName)).isTrue()
+    }
+
+    @Test
+    fun testSwipedOutNotification_notPinnedByUser() {
+        val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+        underTest.showNotification(entry, isPinnedByUser = false)
+        underTest.addSwipedOutNotification(entry.key)
+
+        // Remove should succeed because the notification is swiped out
+        val removedImmediately =
+            underTest.removeNotification(
+                entry.key,
+                /* releaseImmediately= */ false,
+                /* reason= */ "swipe out",
+            )
+        assertThat(removedImmediately).isTrue()
+        assertThat(underTest.isHeadsUpEntry(entry.key)).isFalse()
+    }
+
+    @Test
+    @EnableFlags(StatusBarNotifChips.FLAG_NAME)
+    fun testSwipedOutNotification_isPinnedByUser() {
+        val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+        underTest.showNotification(entry, isPinnedByUser = true)
         underTest.addSwipedOutNotification(entry.key)
 
         // Remove should succeed because the notification is swiped out
@@ -413,10 +548,24 @@
     }
 
     @Test
-    fun testExtendHeadsUp() {
+    fun testExtendHeadsUp_notPinnedByUser() {
         val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
-        underTest.showNotification(entry)
+        underTest.showNotification(entry, isPinnedByUser = false)
+
         underTest.extendHeadsUp()
+
+        systemClock.advanceTime(((TEST_AUTO_DISMISS_TIME + TEST_EXTENSION_TIME) / 2).toLong())
+        assertThat(underTest.isHeadsUpEntry(entry.key)).isTrue()
+    }
+
+    @Test
+    @EnableFlags(StatusBarNotifChips.FLAG_NAME)
+    fun testExtendHeadsUp_isPinnedByUser() {
+        val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+        underTest.showNotification(entry, isPinnedByUser = true)
+
+        underTest.extendHeadsUp()
+
         systemClock.advanceTime(((TEST_AUTO_DISMISS_TIME + TEST_EXTENSION_TIME) / 2).toLong())
         assertThat(underTest.isHeadsUpEntry(entry.key)).isTrue()
     }
@@ -465,7 +614,7 @@
         kosmos.visualStabilityProvider.isReorderingAllowed = true
 
         val notifEntry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
-        underTest.showNotification(notifEntry)
+        underTest.showNotification(notifEntry, isPinnedByUser = false)
         assertThat(notifEntry.isSeenInShade).isFalse()
     }
 
@@ -604,7 +753,7 @@
                 HeadsUpManagerTestUtil.createFullScreenIntentEntry(/* id= */ 0, mContext)
 
             // Add notifEntry to ANM mAlertEntries map and make it NOT unpinned
-            underTest.showNotification(notifEntry)
+            underTest.showNotification(notifEntry, isPinnedByUser = false)
 
             val headsUpEntry = underTest.getHeadsUpEntry(notifEntry.key)
             headsUpEntry!!.mWasUnpinned = false
@@ -621,7 +770,7 @@
                 HeadsUpManagerTestUtil.createFullScreenIntentEntry(/* id= */ 0, mContext)
 
             // Add notifEntry to ANM mAlertEntries map and make it unpinned
-            underTest.showNotification(notifEntry)
+            underTest.showNotification(notifEntry, isPinnedByUser = false)
 
             val headsUpEntry = underTest.getHeadsUpEntry(notifEntry.key)
             headsUpEntry!!.mWasUnpinned = true
@@ -793,11 +942,11 @@
             )
 
         // Note: the standard way to show a notification would be calling showNotification rather
-        // than onAlertEntryAdded. However, in practice showNotification in effect adds
+        // than onEntryAdded. However, in practice showNotification in effect adds
         // the notification and then updates it; in order to not log twice, the entry needs
         // to have a functional ExpandableNotificationRow that can keep track of whether it's
         // pinned or not (via isRowPinned()). That feels like a lot to pull in to test this one bit.
-        underTest.onEntryAdded(entryToPin)
+        underTest.onEntryAdded(entryToPin, /* requestedPinnedStatus= */ PinnedStatus.PinnedBySystem)
 
         assertThat(uiEventLoggerFake.numLogs()).isEqualTo(2)
         assertThat(AvalancheController.ThrottleEvent.AVALANCHE_THROTTLING_HUN_SHOWN.getId())
@@ -816,11 +965,11 @@
             )
 
         // Note: the standard way to show a notification would be calling showNotification rather
-        // than onAlertEntryAdded. However, in practice showNotification in effect adds
+        // than onEntryAdded. However, in practice showNotification in effect adds
         // the notification and then updates it; in order to not log twice, the entry needs
         // to have a functional ExpandableNotificationRow that can keep track of whether it's
         // pinned or not (via isRowPinned()). That feels like a lot to pull in to test this one bit.
-        underTest.onEntryAdded(entryToPin)
+        underTest.onEntryAdded(entryToPin, /* requestedPinnedStatus= */ PinnedStatus.PinnedBySystem)
 
         assertThat(uiEventLoggerFake.numLogs()).isEqualTo(1)
         assertThat(HeadsUpManagerImpl.NotificationPeekEvent.NOTIFICATION_PEEK.id)
@@ -889,7 +1038,10 @@
         val flags: List<FlagsParameterization>
             get() = buildList {
                 addAll(
-                    FlagsParameterization.allCombinationsOf(NotificationThrottleHun.FLAG_NAME)
+                    FlagsParameterization.allCombinationsOf(
+                            NotificationThrottleHun.FLAG_NAME,
+                            StatusBarNotifChips.FLAG_NAME,
+                        )
                         .andSceneContainer()
                 )
             }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinderTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinderTest.java
index af2789b..f7673da 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinderTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinderTest.java
@@ -75,8 +75,8 @@
             return new CancellationSignal();
         });
 
-        mViewBinder.bindHeadsUpView(mEntry, null);
-        verify(mLogger).startBindingHun(eq(mEntry));
+        mViewBinder.bindHeadsUpView(mEntry, /* isPinnedByUser= */ false, null);
+        verify(mLogger).startBindingHun(mEntry, /* isPinnedByUser= */ false);
         verifyNoMoreInteractions(mLogger);
         clearInvocations(mLogger);
 
@@ -85,8 +85,8 @@
         verifyNoMoreInteractions(mLogger);
         clearInvocations(mLogger);
 
-        mViewBinder.bindHeadsUpView(mEntry, null);
-        verify(mLogger).startBindingHun(eq(mEntry));
+        mViewBinder.bindHeadsUpView(mEntry, /* isPinnedByUser= */ true, null);
+        verify(mLogger).startBindingHun(mEntry, /* isPinnedByUser= */ true);
         verifyNoMoreInteractions(mLogger);
         clearInvocations(mLogger);
 
@@ -116,8 +116,8 @@
             return new CancellationSignal();
         });
 
-        mViewBinder.bindHeadsUpView(mEntry, null);
-        verify(mLogger).startBindingHun(eq(mEntry));
+        mViewBinder.bindHeadsUpView(mEntry, /* isPinnedByUser= */ false, null);
+        verify(mLogger).startBindingHun(mEntry, /* isPinnedByUser= */ false);
         verifyNoMoreInteractions(mLogger);
         clearInvocations(mLogger);
 
@@ -140,8 +140,8 @@
             return new CancellationSignal();
         });
 
-        mViewBinder.bindHeadsUpView(mEntry, null);
-        verify(mLogger).startBindingHun(eq(mEntry));
+        mViewBinder.bindHeadsUpView(mEntry, /* isPinnedByUser= */ true, null);
+        verify(mLogger).startBindingHun(mEntry, /* isPinnedByUser= */ true);
         verifyNoMoreInteractions(mLogger);
         clearInvocations(mLogger);
 
@@ -167,8 +167,8 @@
             return new CancellationSignal();
         });
 
-        mViewBinder.bindHeadsUpView(mEntry, null);
-        verify(mLogger).startBindingHun(eq(mEntry));
+        mViewBinder.bindHeadsUpView(mEntry, /* isPinnedByUser= */ false, null);
+        verify(mLogger).startBindingHun(mEntry, /* isPinnedByUser= */ false);
         verifyNoMoreInteractions(mLogger);
         clearInvocations(mLogger);
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorTest.kt
index 6736ccf..abd0a28 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorTest.kt
@@ -101,6 +101,7 @@
                     setSubText(TEST_SUB_TEXT)
                     setContentTitle(TEST_CONTENT_TITLE)
                     setContentText(TEST_CONTENT_TEXT)
+                    setShortCriticalText(TEST_SHORT_CRITICAL_TEXT)
                 }
                 .also { provider.promotedEntries.add(it) }
 
@@ -114,6 +115,52 @@
 
     @Test
     @EnableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME)
+    @DisableFlags(android.app.Flags.FLAG_API_RICH_ONGOING)
+    fun extractContent_apiFlagOff_shortCriticalTextNotExtracted() {
+        val entry =
+            createEntry { setShortCriticalText(TEST_SHORT_CRITICAL_TEXT) }
+                .also { provider.promotedEntries.add(it) }
+
+        val content = extractContent(entry)
+
+        assertThat(content).isNotNull()
+        assertThat(content?.text).isNull()
+    }
+
+    @Test
+    @EnableFlags(
+        PromotedNotificationUi.FLAG_NAME,
+        StatusBarNotifChips.FLAG_NAME,
+        android.app.Flags.FLAG_API_RICH_ONGOING,
+    )
+    fun extractContent_apiFlagOn_shortCriticalTextExtracted() {
+        val entry =
+            createEntry { setShortCriticalText(TEST_SHORT_CRITICAL_TEXT) }
+                .also { provider.promotedEntries.add(it) }
+
+        val content = extractContent(entry)
+
+        assertThat(content).isNotNull()
+        assertThat(content?.shortCriticalText).isEqualTo(TEST_SHORT_CRITICAL_TEXT)
+    }
+
+    @Test
+    @EnableFlags(
+        PromotedNotificationUi.FLAG_NAME,
+        StatusBarNotifChips.FLAG_NAME,
+        android.app.Flags.FLAG_API_RICH_ONGOING,
+    )
+    fun extractContent_noShortCriticalTextSet_textIsNull() {
+        val entry = createEntry {}.also { provider.promotedEntries.add(it) }
+
+        val content = extractContent(entry)
+
+        assertThat(content).isNotNull()
+        assertThat(content?.shortCriticalText).isNull()
+    }
+
+    @Test
+    @EnableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME)
     fun extractContent_fromBigPictureStyle() {
         val entry =
             createEntry { setStyle(BigPictureStyle()) }.also { provider.promotedEntries.add(it) }
@@ -201,6 +248,7 @@
         private const val TEST_SUB_TEXT = "sub text"
         private const val TEST_CONTENT_TITLE = "content title"
         private const val TEST_CONTENT_TEXT = "content text"
+        private const val TEST_SHORT_CRITICAL_TEXT = "short"
 
         private const val TEST_PERSON_NAME = "person name"
         private const val TEST_PERSON_KEY = "person key"
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ConfigurationControllerImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ConfigurationControllerImplTest.kt
index 942ea65..e87077d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ConfigurationControllerImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ConfigurationControllerImplTest.kt
@@ -21,11 +21,13 @@
 import android.content.res.Configuration.UI_MODE_NIGHT_YES
 import android.content.res.Configuration.UI_MODE_TYPE_CAR
 import android.os.LocaleList
+import android.view.Display
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener
 import com.google.common.truth.Truth.assertThat
+import java.util.Locale
 import org.junit.Before
 import org.junit.Ignore
 import org.junit.Test
@@ -34,7 +36,6 @@
 import org.mockito.Mockito.mock
 import org.mockito.Mockito.never
 import org.mockito.Mockito.verify
-import java.util.Locale
 
 @RunWith(AndroidJUnit4::class)
 @SmallTest
@@ -64,9 +65,11 @@
         mConfigurationController.addCallback(listener2)
 
         doAnswer {
-            mConfigurationController.removeCallback(listener2)
-            null
-        }.`when`(listener).onThemeChanged()
+                mConfigurationController.removeCallback(listener2)
+                null
+            }
+            .`when`(listener)
+            .onThemeChanged()
 
         mConfigurationController.notifyThemeChanged()
         verify(listener).onThemeChanged()
@@ -208,7 +211,6 @@
         assertThat(listener.maxBoundsChanged).isTrue()
     }
 
-
     @Test
     fun localeListChanged_listenerNotified() {
         val config = mContext.resources.configuration
@@ -289,7 +291,6 @@
         assertThat(listener.orientationChanged).isTrue()
     }
 
-
     @Test
     fun multipleUpdates_listenerNotifiedOfAll() {
         val config = mContext.resources.configuration
@@ -313,6 +314,17 @@
     }
 
     @Test
+    fun onMovedToDisplay_dispatchedToChildren() {
+        val config = mContext.resources.configuration
+        val listener = createAndAddListener()
+
+        mConfigurationController.dispatchOnMovedToDisplay(newDisplayId = 1, config)
+
+        assertThat(listener.display).isEqualTo(1)
+        assertThat(listener.changedConfig).isEqualTo(config)
+    }
+
+    @Test
     @Ignore("b/261408895")
     fun equivalentConfigObject_listenerNotNotified() {
         val config = mContext.resources.configuration
@@ -343,35 +355,49 @@
         var localeListChanged = false
         var layoutDirectionChanged = false
         var orientationChanged = false
+        var display = Display.DEFAULT_DISPLAY
 
         override fun onConfigChanged(newConfig: Configuration?) {
             changedConfig = newConfig
         }
+
         override fun onDensityOrFontScaleChanged() {
             densityOrFontScaleChanged = true
         }
+
         override fun onSmallestScreenWidthChanged() {
             smallestScreenWidthChanged = true
         }
+
         override fun onMaxBoundsChanged() {
             maxBoundsChanged = true
         }
+
         override fun onUiModeChanged() {
             uiModeChanged = true
         }
+
         override fun onThemeChanged() {
             themeChanged = true
         }
+
         override fun onLocaleListChanged() {
             localeListChanged = true
         }
+
         override fun onLayoutDirectionChanged(isLayoutRtl: Boolean) {
             layoutDirectionChanged = true
         }
+
         override fun onOrientationChanged(orientation: Int) {
             orientationChanged = true
         }
 
+        override fun onMovedToDisplay(newDisplayId: Int, newConfiguration: Configuration?) {
+            display = newDisplayId
+            changedConfig = newConfiguration
+        }
+
         fun assertNoMethodsCalled() {
             assertThat(densityOrFontScaleChanged).isFalse()
             assertThat(smallestScreenWidthChanged).isFalse()
@@ -391,6 +417,7 @@
             themeChanged = false
             localeListChanged = false
             layoutDirectionChanged = false
+            display = Display.DEFAULT_DISPLAY
         }
     }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.kt
index a9afb06..f4c2545 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.kt
@@ -16,6 +16,7 @@
 package com.android.systemui.statusbar.phone
 
 import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
 import android.testing.TestableLooper
 import android.testing.TestableLooper.RunWithLooper
 import android.view.View
@@ -23,12 +24,15 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.kosmos.collectLastValue
+import com.android.systemui.kosmos.runTest
 import com.android.systemui.kosmos.useUnconfinedTestDispatcher
 import com.android.systemui.plugins.fakeDarkIconDispatcher
 import com.android.systemui.plugins.statusbar.statusBarStateController
 import com.android.systemui.shade.ShadeHeadsUpTracker
 import com.android.systemui.shade.shadeViewController
 import com.android.systemui.statusbar.HeadsUpStatusBarView
+import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
 import com.android.systemui.statusbar.commandQueue
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
 import com.android.systemui.statusbar.notification.domain.interactor.HeadsUpNotificationIconInteractor
@@ -114,65 +118,172 @@
     }
 
     @Test
-    fun testShowinEntryUpdated() {
+    fun showingEntryUpdated_whenPinnedBySystem() {
         row.setPinnedStatus(PinnedStatus.PinnedBySystem)
-        whenever(headsUpManager.hasPinnedHeadsUp()).thenReturn(true)
-        whenever(headsUpManager.getTopEntry()).thenReturn(entry)
+        setHeadsUpNotifOnManager(entry)
         underTest.onHeadsUpPinned(entry)
+
         assertThat(headsUpStatusBarView.showingEntry).isEqualTo(row.entry)
 
         row.setPinnedStatus(PinnedStatus.NotPinned)
-        whenever(headsUpManager.hasPinnedHeadsUp()).thenReturn(false)
+        setHeadsUpNotifOnManager(null)
         underTest.onHeadsUpUnPinned(entry)
+
         assertThat(headsUpStatusBarView.showingEntry).isNull()
     }
 
     @Test
-    fun testPinnedStatusUpdated() {
+    @DisableFlags(StatusBarNotifChips.FLAG_NAME)
+    fun showingEntryUpdated_whenPinnedByUser_andFlagOff() {
+        row.setPinnedStatus(PinnedStatus.PinnedByUser)
+        setHeadsUpNotifOnManager(entry)
+        underTest.onHeadsUpPinned(entry)
+
+        assertThat(headsUpStatusBarView.showingEntry).isEqualTo(row.entry)
+    }
+
+    @Test
+    @EnableFlags(StatusBarNotifChips.FLAG_NAME)
+    fun showingEntryNotUpdated_whenPinnedByUser_andFlagOn() {
+        // WHEN the HUN was pinned by the user
+        row.setPinnedStatus(PinnedStatus.PinnedByUser)
+        setHeadsUpNotifOnManager(entry)
+        underTest.onHeadsUpPinned(entry)
+
+        // THEN we don't show the HUN status bar view
+        assertThat(headsUpStatusBarView.showingEntry).isNull()
+    }
+
+    @Test
+    fun pinnedStatusUpdatedToSystem_whenPinnedBySystem() {
         row.setPinnedStatus(PinnedStatus.PinnedBySystem)
-        whenever(headsUpManager.hasPinnedHeadsUp()).thenReturn(true)
-        whenever(headsUpManager.getTopEntry()).thenReturn(entry)
+        setHeadsUpNotifOnManager(entry)
         underTest.onHeadsUpPinned(entry)
         assertThat(underTest.pinnedStatus).isEqualTo(PinnedStatus.PinnedBySystem)
 
         row.setPinnedStatus(PinnedStatus.NotPinned)
-        whenever(headsUpManager.hasPinnedHeadsUp()).thenReturn(false)
+        setHeadsUpNotifOnManager(null)
         underTest.onHeadsUpUnPinned(entry)
         assertThat(underTest.pinnedStatus).isEqualTo(PinnedStatus.NotPinned)
     }
 
     @Test
+    @EnableFlags(StatusBarNotifChips.FLAG_NAME)
+    fun pinnedStatusUpdatedToNotPinned_whenPinnedByUser_andFlagOn() {
+        row.setPinnedStatus(PinnedStatus.PinnedByUser)
+        setHeadsUpNotifOnManager(entry)
+        underTest.onHeadsUpPinned(entry)
+
+        // It's unintuitive that the pinnedStatus wouldn't match the status on the notification.
+        // Explanation: HeadsUpAppearanceController#updateTopEntry doesn't do anything if
+        // HeadsUpManager.pinnedHeadsUpStatus != PinnedBySystem. So when we're PinnedByUser,
+        // HeadsUpAppearanceController early-returns before even updating the pinned status.
+        assertThat(underTest.pinnedStatus).isEqualTo(PinnedStatus.NotPinned)
+    }
+
+    @Test
+    fun isolatedIconSet_whenPinnedBySystem() =
+        kosmos.runTest {
+            val latestIsolatedIcon by
+                collectLastValue(kosmos.headsUpNotificationIconInteractor.isolatedNotification)
+
+            row.setPinnedStatus(PinnedStatus.PinnedBySystem)
+            setHeadsUpNotifOnManager(entry)
+            underTest.onHeadsUpPinned(entry)
+
+            assertThat(latestIsolatedIcon).isEqualTo(entry.key)
+
+            row.setPinnedStatus(PinnedStatus.NotPinned)
+            setHeadsUpNotifOnManager(null)
+            underTest.onHeadsUpUnPinned(entry)
+
+            assertThat(latestIsolatedIcon).isNull()
+        }
+
+    @Test
+    @DisableFlags(StatusBarNotifChips.FLAG_NAME)
+    fun isolatedIconSet_whenPinnedByUser_andFlagOff() =
+        kosmos.runTest {
+            val latestIsolatedIcon by
+                collectLastValue(kosmos.headsUpNotificationIconInteractor.isolatedNotification)
+
+            row.setPinnedStatus(PinnedStatus.PinnedByUser)
+            setHeadsUpNotifOnManager(entry)
+            underTest.onHeadsUpPinned(entry)
+
+            assertThat(latestIsolatedIcon).isEqualTo(entry.key)
+        }
+
+    @Test
+    @EnableFlags(StatusBarNotifChips.FLAG_NAME)
+    fun isolatedIconNotSet_whenPinnedByUser_andFlagOn() =
+        kosmos.runTest {
+            val latestIsolatedIcon by
+                collectLastValue(kosmos.headsUpNotificationIconInteractor.isolatedNotification)
+
+            row.setPinnedStatus(PinnedStatus.PinnedByUser)
+            setHeadsUpNotifOnManager(entry)
+            underTest.onHeadsUpPinned(entry)
+
+            assertThat(latestIsolatedIcon).isNull()
+        }
+
+    @Test
     @DisableFlags(AsyncGroupHeaderViewInflation.FLAG_NAME)
     fun testHeaderUpdated() {
         row.setPinnedStatus(PinnedStatus.PinnedBySystem)
-        whenever(headsUpManager.hasPinnedHeadsUp()).thenReturn(true)
-        whenever(headsUpManager.getTopEntry()).thenReturn(entry)
+        setHeadsUpNotifOnManager(entry)
         underTest.onHeadsUpPinned(entry)
         assertThat(row.headerVisibleAmount).isEqualTo(0.0f)
 
         row.setPinnedStatus(PinnedStatus.NotPinned)
-        whenever(headsUpManager.hasPinnedHeadsUp()).thenReturn(false)
+        setHeadsUpNotifOnManager(null)
         underTest.onHeadsUpUnPinned(entry)
         assertThat(row.headerVisibleAmount).isEqualTo(1.0f)
     }
 
     @Test
-    fun testOperatorNameViewUpdated() {
+    fun operatorNameViewUpdated_whenPinnedBySystem() {
         underTest.setAnimationsEnabled(false)
 
         row.setPinnedStatus(PinnedStatus.PinnedBySystem)
-        whenever(headsUpManager.hasPinnedHeadsUp()).thenReturn(true)
-        whenever(headsUpManager.getTopEntry()).thenReturn(entry)
+        setHeadsUpNotifOnManager(entry)
         underTest.onHeadsUpPinned(entry)
         assertThat(operatorNameView.visibility).isEqualTo(View.INVISIBLE)
 
         row.setPinnedStatus(PinnedStatus.NotPinned)
-        whenever(headsUpManager.hasPinnedHeadsUp()).thenReturn(false)
+        setHeadsUpNotifOnManager(null)
         underTest.onHeadsUpUnPinned(entry)
         assertThat(operatorNameView.visibility).isEqualTo(View.VISIBLE)
     }
 
     @Test
+    @DisableFlags(StatusBarNotifChips.FLAG_NAME)
+    fun operatorNameViewUpdated_whenPinnedByUser_andFlagOff() {
+        underTest.setAnimationsEnabled(false)
+
+        row.setPinnedStatus(PinnedStatus.PinnedByUser)
+        setHeadsUpNotifOnManager(entry)
+        underTest.onHeadsUpPinned(entry)
+
+        assertThat(operatorNameView.visibility).isEqualTo(View.INVISIBLE)
+    }
+
+    @Test
+    @EnableFlags(StatusBarNotifChips.FLAG_NAME)
+    fun operatorNameViewNotUpdated_whenPinnedByUser_andFlagOn() {
+        underTest.setAnimationsEnabled(false)
+
+        // WHEN the row was pinned by the user
+        row.setPinnedStatus(PinnedStatus.PinnedByUser)
+        setHeadsUpNotifOnManager(entry)
+        underTest.onHeadsUpPinned(entry)
+
+        // THEN we don't need to hide the operator name view
+        assertThat(operatorNameView.visibility).isEqualTo(View.VISIBLE)
+    }
+
+    @Test
     fun constructor_animationValuesUpdated() {
         val appearFraction = .75f
         val expandedHeight = 400f
@@ -276,4 +387,16 @@
 
         verify(phoneStatusBarTransitions).onHeadsUpStateChanged(false)
     }
+
+    private fun setHeadsUpNotifOnManager(entry: NotificationEntry?) {
+        if (entry != null) {
+            whenever(headsUpManager.hasPinnedHeadsUp()).thenReturn(true)
+            whenever(headsUpManager.pinnedHeadsUpStatus()).thenReturn(entry.pinnedStatus)
+            whenever(headsUpManager.getTopEntry()).thenReturn(entry)
+        } else {
+            whenever(headsUpManager.hasPinnedHeadsUp()).thenReturn(false)
+            whenever(headsUpManager.pinnedHeadsUpStatus()).thenReturn(PinnedStatus.NotPinned)
+            whenever(headsUpManager.getTopEntry()).thenReturn(null)
+        }
+    }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
deleted file mode 100644
index 41782a1..0000000
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
+++ /dev/null
@@ -1,379 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the specific language governing
- * permissions and limitations under the License.
- */
-
-package com.android.systemui.statusbar.phone;
-
-import static android.view.Display.DEFAULT_DISPLAY;
-
-import static com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.BUBBLE;
-import static com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.PEEK;
-import static com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.PULSE;
-
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.app.Notification;
-import android.app.PendingIntent;
-import android.app.StatusBarManager;
-import android.platform.test.annotations.DisableFlags;
-import android.platform.test.annotations.EnableFlags;
-import android.testing.TestableLooper;
-import android.testing.TestableLooper.RunWithLooper;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.InitController;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.power.domain.interactor.PowerInteractor;
-import com.android.systemui.settings.FakeDisplayTracker;
-import com.android.systemui.shade.NotificationShadeWindowView;
-import com.android.systemui.shade.QuickSettingsController;
-import com.android.systemui.shade.ShadeController;
-import com.android.systemui.shade.ShadeViewController;
-import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor;
-import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.LockscreenShadeTransitionController;
-import com.android.systemui.statusbar.NotificationLockscreenUserManager;
-import com.android.systemui.statusbar.NotificationMediaManager;
-import com.android.systemui.statusbar.NotificationRemoteInputManager;
-import com.android.systemui.statusbar.NotificationShadeWindowController;
-import com.android.systemui.statusbar.SysuiStatusBarStateController;
-import com.android.systemui.statusbar.notification.DynamicPrivacyController;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
-import com.android.systemui.statusbar.notification.collection.render.NotifShadeEventSource;
-import com.android.systemui.statusbar.notification.domain.interactor.NotificationAlertsInteractor;
-import com.android.systemui.statusbar.notification.interruption.NotificationInterruptSuppressor;
-import com.android.systemui.statusbar.notification.interruption.VisualInterruptionCondition;
-import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProvider;
-import com.android.systemui.statusbar.notification.interruption.VisualInterruptionFilter;
-import com.android.systemui.statusbar.notification.interruption.VisualInterruptionRefactor;
-import com.android.systemui.statusbar.notification.interruption.VisualInterruptionType;
-import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
-import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
-import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
-import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
-import com.android.systemui.statusbar.notification.headsup.HeadsUpManager;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-
-import java.util.List;
-import java.util.Set;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-@RunWithLooper()
-public class StatusBarNotificationPresenterTest extends SysuiTestCase {
-    private StatusBarNotificationPresenter mStatusBarNotificationPresenter;
-    private final VisualInterruptionDecisionProvider mVisualInterruptionDecisionProvider =
-            mock(VisualInterruptionDecisionProvider.class);
-    private NotificationInterruptSuppressor mInterruptSuppressor;
-    private VisualInterruptionCondition mAlertsDisabledCondition;
-    private VisualInterruptionCondition mVrModeCondition;
-    private VisualInterruptionFilter mNeedsRedactionFilter;
-    private VisualInterruptionCondition mPanelsDisabledCondition;
-    private CommandQueue mCommandQueue;
-    private final ShadeController mShadeController = mock(ShadeController.class);
-    private final NotificationAlertsInteractor mNotificationAlertsInteractor =
-            mock(NotificationAlertsInteractor.class);
-    private final KeyguardStateController mKeyguardStateController =
-            mock(KeyguardStateController.class);
-
-    @Before
-    public void setup() {
-        mCommandQueue = new CommandQueue(mContext, new FakeDisplayTracker(mContext));
-        mDependency.injectTestDependency(StatusBarStateController.class,
-                mock(SysuiStatusBarStateController.class));
-        mDependency.injectTestDependency(ShadeController.class, mShadeController);
-        mDependency.injectMockDependency(NotificationRemoteInputManager.Callback.class);
-        mDependency.injectMockDependency(NotificationShadeWindowController.class);
-
-        when(mNotificationAlertsInteractor.areNotificationAlertsEnabled()).thenReturn(true);
-
-        createPresenter();
-        if (VisualInterruptionRefactor.isEnabled()) {
-            verifyAndCaptureSuppressors();
-        } else {
-            verifyAndCaptureLegacySuppressor();
-        }
-    }
-
-    @Test
-    @DisableFlags(VisualInterruptionRefactor.FLAG_NAME)
-    public void testInit_refactorDisabled() {
-        assertFalse(VisualInterruptionRefactor.isEnabled());
-        assertNull(mAlertsDisabledCondition);
-        assertNull(mVrModeCondition);
-        assertNull(mNeedsRedactionFilter);
-        assertNull(mPanelsDisabledCondition);
-        assertNotNull(mInterruptSuppressor);
-    }
-
-    @Test
-    @EnableFlags(VisualInterruptionRefactor.FLAG_NAME)
-    public void testInit_refactorEnabled() {
-        assertTrue(VisualInterruptionRefactor.isEnabled());
-        assertNotNull(mAlertsDisabledCondition);
-        assertNotNull(mVrModeCondition);
-        assertNotNull(mNeedsRedactionFilter);
-        assertNotNull(mPanelsDisabledCondition);
-        assertNull(mInterruptSuppressor);
-    }
-
-    @Test
-    @DisableFlags(VisualInterruptionRefactor.FLAG_NAME)
-    public void testNoSuppressHeadsUp_default_refactorDisabled() {
-        assertFalse(mInterruptSuppressor.suppressAwakeHeadsUp(createNotificationEntry()));
-    }
-
-    @Test
-    @EnableFlags(VisualInterruptionRefactor.FLAG_NAME)
-    public void testNoSuppressHeadsUp_default_refactorEnabled() {
-        assertFalse(mAlertsDisabledCondition.shouldSuppress());
-        assertFalse(mVrModeCondition.shouldSuppress());
-        assertFalse(mNeedsRedactionFilter.shouldSuppress(createNotificationEntry()));
-        assertFalse(mAlertsDisabledCondition.shouldSuppress());
-    }
-
-    @Test
-    @DisableFlags(VisualInterruptionRefactor.FLAG_NAME)
-    public void testSuppressHeadsUp_disabledStatusBar_refactorDisabled() {
-        mCommandQueue.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_EXPAND, 0,
-                false /* animate */);
-        TestableLooper.get(this).processAllMessages();
-
-        assertTrue("The panel should suppress heads up while disabled",
-                mInterruptSuppressor.suppressAwakeHeadsUp(createNotificationEntry()));
-    }
-
-    @Test
-    @EnableFlags(VisualInterruptionRefactor.FLAG_NAME)
-    public void testSuppressHeadsUp_disabledStatusBar_refactorEnabled() {
-        mCommandQueue.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_EXPAND, 0,
-                false /* animate */);
-        TestableLooper.get(this).processAllMessages();
-
-        assertTrue("The panel should suppress heads up while disabled",
-                mPanelsDisabledCondition.shouldSuppress());
-    }
-
-    @Test
-    @DisableFlags(VisualInterruptionRefactor.FLAG_NAME)
-    public void testSuppressHeadsUp_disabledNotificationShade_refactorDisabled() {
-        mCommandQueue.disable(DEFAULT_DISPLAY, 0, StatusBarManager.DISABLE2_NOTIFICATION_SHADE,
-                false /* animate */);
-        TestableLooper.get(this).processAllMessages();
-
-        assertTrue("The panel should suppress interruptions while notification shade disabled",
-                mInterruptSuppressor.suppressAwakeHeadsUp(createNotificationEntry()));
-    }
-
-    @Test
-    @EnableFlags(VisualInterruptionRefactor.FLAG_NAME)
-    public void testSuppressHeadsUp_disabledNotificationShade_refactorEnabled() {
-        mCommandQueue.disable(DEFAULT_DISPLAY, 0, StatusBarManager.DISABLE2_NOTIFICATION_SHADE,
-                false /* animate */);
-        TestableLooper.get(this).processAllMessages();
-
-        assertTrue("The panel should suppress interruptions while notification shade disabled",
-                mPanelsDisabledCondition.shouldSuppress());
-    }
-
-    @Test
-    @EnableFlags(VisualInterruptionRefactor.FLAG_NAME)
-    public void testPanelsDisabledConditionSuppressesPeek() {
-        final Set<VisualInterruptionType> types = mPanelsDisabledCondition.getTypes();
-        assertTrue(types.contains(PEEK));
-        assertFalse(types.contains(PULSE));
-        assertFalse(types.contains(BUBBLE));
-    }
-
-    @Test
-    @DisableFlags(VisualInterruptionRefactor.FLAG_NAME)
-    public void testNoSuppressHeadsUp_FSI_nonOccludedKeyguard_refactorDisabled() {
-        when(mKeyguardStateController.isShowing()).thenReturn(true);
-        when(mKeyguardStateController.isOccluded()).thenReturn(false);
-
-        assertFalse(mInterruptSuppressor.suppressAwakeHeadsUp(createFsiNotificationEntry()));
-    }
-
-    @Test
-    @EnableFlags(VisualInterruptionRefactor.FLAG_NAME)
-    public void testNoSuppressHeadsUp_FSI_nonOccludedKeyguard_refactorEnabled() {
-        when(mKeyguardStateController.isShowing()).thenReturn(true);
-        when(mKeyguardStateController.isOccluded()).thenReturn(false);
-
-        assertFalse(mNeedsRedactionFilter.shouldSuppress(createFsiNotificationEntry()));
-
-        final Set<VisualInterruptionType> types = mNeedsRedactionFilter.getTypes();
-        assertTrue(types.contains(PEEK));
-        assertFalse(types.contains(PULSE));
-        assertFalse(types.contains(BUBBLE));
-    }
-
-    @Test
-    @DisableFlags(VisualInterruptionRefactor.FLAG_NAME)
-    public void testSuppressInterruptions_vrMode_refactorDisabled() {
-        mStatusBarNotificationPresenter.mVrMode = true;
-
-        assertTrue("Vr mode should suppress interruptions",
-                mInterruptSuppressor.suppressAwakeInterruptions(createNotificationEntry()));
-    }
-
-    @Test
-    @EnableFlags(VisualInterruptionRefactor.FLAG_NAME)
-    public void testSuppressInterruptions_vrMode_refactorEnabled() {
-        mStatusBarNotificationPresenter.mVrMode = true;
-
-        assertTrue("Vr mode should suppress interruptions", mVrModeCondition.shouldSuppress());
-
-        final Set<VisualInterruptionType> types = mVrModeCondition.getTypes();
-        assertTrue(types.contains(PEEK));
-        assertFalse(types.contains(PULSE));
-        assertTrue(types.contains(BUBBLE));
-    }
-
-    @Test
-    @DisableFlags(VisualInterruptionRefactor.FLAG_NAME)
-    public void testSuppressInterruptions_statusBarAlertsDisabled_refactorDisabled() {
-        when(mNotificationAlertsInteractor.areNotificationAlertsEnabled()).thenReturn(false);
-
-        assertTrue("When alerts aren't enabled, interruptions are suppressed",
-                mInterruptSuppressor.suppressInterruptions(createNotificationEntry()));
-    }
-
-    @Test
-    @EnableFlags(VisualInterruptionRefactor.FLAG_NAME)
-    public void testSuppressInterruptions_statusBarAlertsDisabled_refactorEnabled() {
-        when(mNotificationAlertsInteractor.areNotificationAlertsEnabled()).thenReturn(false);
-
-        assertTrue("When alerts aren't enabled, interruptions are suppressed",
-                mAlertsDisabledCondition.shouldSuppress());
-
-        final Set<VisualInterruptionType> types = mAlertsDisabledCondition.getTypes();
-        assertTrue(types.contains(PEEK));
-        assertTrue(types.contains(PULSE));
-        assertTrue(types.contains(BUBBLE));
-    }
-
-    private void createPresenter() {
-        final ShadeViewController shadeViewController = mock(ShadeViewController.class);
-
-        final NotificationShadeWindowView notificationShadeWindowView =
-                mock(NotificationShadeWindowView.class);
-        when(notificationShadeWindowView.getResources()).thenReturn(mContext.getResources());
-
-        NotificationStackScrollLayoutController stackScrollLayoutController =
-                mock(NotificationStackScrollLayoutController.class);
-        when(stackScrollLayoutController.getView()).thenReturn(
-                mock(NotificationStackScrollLayout.class));
-
-        final InitController initController = new InitController();
-
-        mStatusBarNotificationPresenter = new StatusBarNotificationPresenter(
-                mContext,
-                shadeViewController,
-                mock(PanelExpansionInteractor.class),
-                mock(QuickSettingsController.class),
-                mock(HeadsUpManager.class),
-                notificationShadeWindowView,
-                mock(ActivityStarter.class),
-                stackScrollLayoutController,
-                mock(DozeScrimController.class),
-                mock(NotificationShadeWindowController.class),
-                mock(DynamicPrivacyController.class),
-                mKeyguardStateController,
-                mNotificationAlertsInteractor,
-                mock(LockscreenShadeTransitionController.class),
-                mock(PowerInteractor.class),
-                mCommandQueue,
-                mock(NotificationLockscreenUserManager.class),
-                mock(SysuiStatusBarStateController.class),
-                mock(NotifShadeEventSource.class),
-                mock(NotificationMediaManager.class),
-                mock(NotificationGutsManager.class),
-                initController,
-                mVisualInterruptionDecisionProvider,
-                mock(NotificationRemoteInputManager.class),
-                mock(NotificationRemoteInputManager.Callback.class),
-                mock(NotificationListContainer.class));
-
-        initController.executePostInitTasks();
-    }
-
-    private void verifyAndCaptureSuppressors() {
-        mInterruptSuppressor = null;
-
-        final ArgumentCaptor<VisualInterruptionCondition> conditionCaptor =
-                ArgumentCaptor.forClass(VisualInterruptionCondition.class);
-        verify(mVisualInterruptionDecisionProvider, times(3)).addCondition(
-                conditionCaptor.capture());
-        final List<VisualInterruptionCondition> conditions = conditionCaptor.getAllValues();
-        mAlertsDisabledCondition = conditions.get(0);
-        mVrModeCondition = conditions.get(1);
-        mPanelsDisabledCondition = conditions.get(2);
-
-        final ArgumentCaptor<VisualInterruptionFilter> needsRedactionFilterCaptor =
-                ArgumentCaptor.forClass(VisualInterruptionFilter.class);
-        verify(mVisualInterruptionDecisionProvider).addFilter(needsRedactionFilterCaptor.capture());
-        mNeedsRedactionFilter = needsRedactionFilterCaptor.getValue();
-    }
-
-    private void verifyAndCaptureLegacySuppressor() {
-        mAlertsDisabledCondition = null;
-        mVrModeCondition = null;
-        mNeedsRedactionFilter = null;
-        mPanelsDisabledCondition = null;
-
-        final ArgumentCaptor<NotificationInterruptSuppressor> suppressorCaptor =
-                ArgumentCaptor.forClass(NotificationInterruptSuppressor.class);
-        verify(mVisualInterruptionDecisionProvider).addLegacySuppressor(suppressorCaptor.capture());
-        mInterruptSuppressor = suppressorCaptor.getValue();
-    }
-
-    private NotificationEntry createNotificationEntry() {
-        return new NotificationEntryBuilder()
-                .setPkg("a")
-                .setOpPkg("a")
-                .setTag("a")
-                .setNotification(new Notification.Builder(getContext(), "a").build())
-                .build();
-    }
-
-    private NotificationEntry createFsiNotificationEntry() {
-        final Notification notification = new Notification.Builder(getContext(), "a")
-                .setFullScreenIntent(mock(PendingIntent.class), true)
-                .build();
-
-        return new NotificationEntryBuilder()
-                .setPkg("a")
-                .setOpPkg("a")
-                .setTag("a")
-                .setNotification(notification)
-                .build();
-    }
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.kt
new file mode 100644
index 0000000..c347347
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.kt
@@ -0,0 +1,454 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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
+
+import android.app.Notification
+import android.app.Notification.Builder
+import android.app.StatusBarManager
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import android.testing.TestableLooper
+import android.testing.TestableLooper.RunWithLooper
+import android.view.Display.DEFAULT_DISPLAY
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.InitController
+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.deviceentry.domain.interactor.deviceUnlockedInteractor
+import com.android.systemui.flags.EnableSceneContainer
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.runTest
+import com.android.systemui.plugins.activityStarter
+import com.android.systemui.power.domain.interactor.powerInteractor
+import com.android.systemui.settings.FakeDisplayTracker
+import com.android.systemui.shade.NotificationShadeWindowView
+import com.android.systemui.shade.ShadeViewController
+import com.android.systemui.shade.domain.interactor.panelExpansionInteractor
+import com.android.systemui.statusbar.CommandQueue
+import com.android.systemui.statusbar.StatusBarState
+import com.android.systemui.statusbar.lockscreenShadeTransitionController
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
+import com.android.systemui.statusbar.notification.domain.interactor.notificationAlertsInteractor
+import com.android.systemui.statusbar.notification.dynamicPrivacyController
+import com.android.systemui.statusbar.notification.headsup.headsUpManager
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptSuppressor
+import com.android.systemui.statusbar.notification.interruption.VisualInterruptionCondition
+import com.android.systemui.statusbar.notification.interruption.VisualInterruptionFilter
+import com.android.systemui.statusbar.notification.interruption.VisualInterruptionRefactor
+import com.android.systemui.statusbar.notification.interruption.VisualInterruptionType
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
+import com.android.systemui.statusbar.notification.stack.notificationStackScrollLayoutController
+import com.android.systemui.statusbar.notification.visualInterruptionDecisionProvider
+import com.android.systemui.statusbar.notificationLockscreenUserManager
+import com.android.systemui.statusbar.notificationRemoteInputManager
+import com.android.systemui.statusbar.notificationShadeWindowController
+import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.statusbar.policy.keyguardStateController
+import com.android.systemui.statusbar.sysuiStatusBarStateController
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.argumentCaptor
+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(AndroidJUnit4::class)
+@RunWithLooper
+class StatusBarNotificationPresenterTest : SysuiTestCase() {
+    private lateinit var kosmos: Kosmos
+
+    private var interruptSuppressor: NotificationInterruptSuppressor? = null
+    private var alertsDisabledCondition: VisualInterruptionCondition? = null
+    private var vrModeCondition: VisualInterruptionCondition? = null
+    private var needsRedactionFilter: VisualInterruptionFilter? = null
+    private var panelsDisabledCondition: VisualInterruptionCondition? = null
+
+    private val commandQueue: CommandQueue = CommandQueue(mContext, FakeDisplayTracker(mContext))
+    private val keyguardStateController: KeyguardStateController
+        get() = kosmos.keyguardStateController
+
+    private val notificationAlertsInteractor
+        get() = kosmos.notificationAlertsInteractor
+
+    private val visualInterruptionDecisionProvider
+        get() = kosmos.visualInterruptionDecisionProvider
+
+    private lateinit var underTest: StatusBarNotificationPresenter
+
+    @Before
+    fun setup() {
+        kosmos =
+            testKosmos().apply {
+                whenever(notificationAlertsInteractor.areNotificationAlertsEnabled())
+                    .thenReturn(true)
+                whenever(notificationStackScrollLayoutController.expandHelperCallback)
+                    .thenReturn(mock())
+                lockscreenShadeTransitionController.setStackScroller(
+                    notificationStackScrollLayoutController
+                )
+                lockscreenShadeTransitionController.centralSurfaces = mock()
+            }
+
+        underTest = createPresenter()
+        if (VisualInterruptionRefactor.isEnabled) {
+            verifyAndCaptureSuppressors()
+        } else {
+            verifyAndCaptureLegacySuppressor()
+        }
+    }
+
+    @Test
+    @DisableFlags(VisualInterruptionRefactor.FLAG_NAME)
+    fun testInit_refactorDisabled() {
+        assertThat(VisualInterruptionRefactor.isEnabled).isFalse()
+        assertThat(alertsDisabledCondition).isNull()
+        assertThat(vrModeCondition).isNull()
+        assertThat(needsRedactionFilter).isNull()
+        assertThat(panelsDisabledCondition).isNull()
+        assertThat(interruptSuppressor).isNotNull()
+    }
+
+    @Test
+    @EnableFlags(VisualInterruptionRefactor.FLAG_NAME)
+    fun testInit_refactorEnabled() {
+        assertThat(VisualInterruptionRefactor.isEnabled).isTrue()
+        assertThat(alertsDisabledCondition).isNotNull()
+        assertThat(vrModeCondition).isNotNull()
+        assertThat(needsRedactionFilter).isNotNull()
+        assertThat(panelsDisabledCondition).isNotNull()
+        assertThat(interruptSuppressor).isNull()
+    }
+
+    @Test
+    @DisableFlags(VisualInterruptionRefactor.FLAG_NAME)
+    fun testNoSuppressHeadsUp_default_refactorDisabled() {
+        assertThat(interruptSuppressor!!.suppressAwakeHeadsUp(createNotificationEntry())).isFalse()
+    }
+
+    @Test
+    @EnableFlags(VisualInterruptionRefactor.FLAG_NAME)
+    fun testNoSuppressHeadsUp_default_refactorEnabled() {
+        assertThat(alertsDisabledCondition!!.shouldSuppress()).isFalse()
+        assertThat(vrModeCondition!!.shouldSuppress()).isFalse()
+        assertThat(needsRedactionFilter!!.shouldSuppress(createNotificationEntry())).isFalse()
+        assertThat(alertsDisabledCondition!!.shouldSuppress()).isFalse()
+    }
+
+    @Test
+    @DisableFlags(VisualInterruptionRefactor.FLAG_NAME)
+    fun testSuppressHeadsUp_disabledStatusBar_refactorDisabled() {
+        commandQueue.disable(
+            DEFAULT_DISPLAY,
+            StatusBarManager.DISABLE_EXPAND,
+            0,
+            false, /* animate */
+        )
+        TestableLooper.get(this).processAllMessages()
+
+        assertWithMessage("The panel should suppress heads up while disabled")
+            .that(interruptSuppressor!!.suppressAwakeHeadsUp(createNotificationEntry()))
+            .isTrue()
+    }
+
+    @Test
+    @EnableFlags(VisualInterruptionRefactor.FLAG_NAME)
+    fun testSuppressHeadsUp_disabledStatusBar_refactorEnabled() {
+        commandQueue.disable(
+            DEFAULT_DISPLAY,
+            StatusBarManager.DISABLE_EXPAND,
+            0,
+            false, /* animate */
+        )
+        TestableLooper.get(this).processAllMessages()
+
+        assertWithMessage("The panel should suppress heads up while disabled")
+            .that(panelsDisabledCondition!!.shouldSuppress())
+            .isTrue()
+    }
+
+    @Test
+    @DisableFlags(VisualInterruptionRefactor.FLAG_NAME)
+    fun testSuppressHeadsUp_disabledNotificationShade_refactorDisabled() {
+        commandQueue.disable(
+            DEFAULT_DISPLAY,
+            0,
+            StatusBarManager.DISABLE2_NOTIFICATION_SHADE,
+            false, /* animate */
+        )
+        TestableLooper.get(this).processAllMessages()
+
+        assertWithMessage(
+                "The panel should suppress interruptions while notification shade disabled"
+            )
+            .that(interruptSuppressor!!.suppressAwakeHeadsUp(createNotificationEntry()))
+            .isTrue()
+    }
+
+    @Test
+    @EnableFlags(VisualInterruptionRefactor.FLAG_NAME)
+    fun testSuppressHeadsUp_disabledNotificationShade_refactorEnabled() {
+        commandQueue.disable(
+            DEFAULT_DISPLAY,
+            0,
+            StatusBarManager.DISABLE2_NOTIFICATION_SHADE,
+            false, /* animate */
+        )
+        TestableLooper.get(this).processAllMessages()
+
+        assertWithMessage(
+                "The panel should suppress interruptions while notification shade disabled"
+            )
+            .that(panelsDisabledCondition!!.shouldSuppress())
+            .isTrue()
+    }
+
+    @Test
+    @EnableFlags(VisualInterruptionRefactor.FLAG_NAME)
+    fun testPanelsDisabledConditionSuppressesPeek() {
+        val types: Set<VisualInterruptionType> = panelsDisabledCondition!!.types
+        assertThat(types).contains(VisualInterruptionType.PEEK)
+        assertThat(types)
+            .containsNoneOf(VisualInterruptionType.BUBBLE, VisualInterruptionType.PULSE)
+    }
+
+    @Test
+    @DisableFlags(VisualInterruptionRefactor.FLAG_NAME)
+    fun testNoSuppressHeadsUp_FSI_nonOccludedKeyguard_refactorDisabled() {
+        whenever(keyguardStateController.isShowing()).thenReturn(true)
+        whenever(keyguardStateController.isOccluded()).thenReturn(false)
+
+        assertThat(interruptSuppressor!!.suppressAwakeHeadsUp(createFsiNotificationEntry()))
+            .isFalse()
+    }
+
+    @Test
+    @EnableFlags(VisualInterruptionRefactor.FLAG_NAME)
+    fun testNoSuppressHeadsUp_FSI_nonOccludedKeyguard_refactorEnabled() {
+        whenever(keyguardStateController.isShowing()).thenReturn(true)
+        whenever(keyguardStateController.isOccluded()).thenReturn(false)
+
+        assertThat(needsRedactionFilter!!.shouldSuppress(createFsiNotificationEntry())).isFalse()
+
+        val types: Set<VisualInterruptionType> = needsRedactionFilter!!.types
+        assertThat(types).contains(VisualInterruptionType.PEEK)
+        assertThat(types)
+            .containsNoneOf(VisualInterruptionType.BUBBLE, VisualInterruptionType.PULSE)
+    }
+
+    @Test
+    @DisableFlags(VisualInterruptionRefactor.FLAG_NAME)
+    fun testSuppressInterruptions_vrMode_refactorDisabled() {
+        underTest.mVrMode = true
+
+        assertWithMessage("Vr mode should suppress interruptions")
+            .that(interruptSuppressor!!.suppressAwakeInterruptions(createNotificationEntry()))
+            .isTrue()
+    }
+
+    @Test
+    @EnableFlags(VisualInterruptionRefactor.FLAG_NAME)
+    fun testSuppressInterruptions_vrMode_refactorEnabled() {
+        underTest.mVrMode = true
+
+        assertWithMessage("Vr mode should suppress interruptions")
+            .that(vrModeCondition!!.shouldSuppress())
+            .isTrue()
+
+        val types: Set<VisualInterruptionType> = vrModeCondition!!.types
+        assertThat(types).contains(VisualInterruptionType.PEEK)
+        assertThat(types).doesNotContain(VisualInterruptionType.PULSE)
+        assertThat(types).contains(VisualInterruptionType.BUBBLE)
+    }
+
+    @Test
+    @DisableFlags(VisualInterruptionRefactor.FLAG_NAME)
+    fun testSuppressInterruptions_statusBarAlertsDisabled_refactorDisabled() {
+        whenever(notificationAlertsInteractor.areNotificationAlertsEnabled()).thenReturn(false)
+
+        assertWithMessage("When alerts aren't enabled, interruptions are suppressed")
+            .that(interruptSuppressor!!.suppressInterruptions(createNotificationEntry()))
+            .isTrue()
+    }
+
+    @Test
+    @EnableFlags(VisualInterruptionRefactor.FLAG_NAME)
+    fun testSuppressInterruptions_statusBarAlertsDisabled_refactorEnabled() {
+        whenever(notificationAlertsInteractor.areNotificationAlertsEnabled()).thenReturn(false)
+
+        assertWithMessage("When alerts aren't enabled, interruptions are suppressed")
+            .that(alertsDisabledCondition!!.shouldSuppress())
+            .isTrue()
+
+        val types: Set<VisualInterruptionType> = alertsDisabledCondition!!.types
+        assertThat(types).contains(VisualInterruptionType.PEEK)
+        assertThat(types).contains(VisualInterruptionType.PULSE)
+        assertThat(types).contains(VisualInterruptionType.BUBBLE)
+    }
+
+    @Test
+    @EnableSceneContainer
+    fun testExpandSensitiveNotification_onLockScreen_opensShade() =
+        kosmos.runTest {
+            // Given we are on the keyguard
+            kosmos.sysuiStatusBarStateController.state = StatusBarState.KEYGUARD
+            // And the device is locked
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
+                AuthenticationMethodModel.Pin
+            )
+
+            // When the user expands a sensitive Notification
+            val entry =
+                createRow().entry.apply {
+                    setSensitive(/* sensitive= */ true, /* deviceSensitive= */ true)
+                }
+            underTest.onExpandClicked(entry, mock(), /* nowExpanded= */ true)
+
+            // Then we open the locked shade
+            assertThat(kosmos.sysuiStatusBarStateController.state)
+                .isEqualTo(StatusBarState.SHADE_LOCKED)
+        }
+
+    @Test
+    @EnableSceneContainer
+    fun testExpandSensitiveNotification_onLockedShade_showsBouncer() =
+        kosmos.runTest {
+            // Given we are on the locked shade
+            kosmos.sysuiStatusBarStateController.state = StatusBarState.SHADE_LOCKED
+            // And the device is locked
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
+                AuthenticationMethodModel.Pin
+            )
+
+            // When the user expands a sensitive Notification
+            val entry =
+                createRow().entry.apply {
+                    setSensitive(/* sensitive= */ true, /* deviceSensitive= */ true)
+                }
+            underTest.onExpandClicked(entry, mock(), /* nowExpanded= */ true)
+
+            // Then we show the bouncer
+            verify(kosmos.activityStarter).dismissKeyguardThenExecute(any(), eq(null), eq(false))
+            // AND we are still on the locked shade
+            assertThat(kosmos.sysuiStatusBarStateController.state)
+                .isEqualTo(StatusBarState.SHADE_LOCKED)
+        }
+
+    private fun createPresenter(): StatusBarNotificationPresenter {
+        val shadeViewController: ShadeViewController = mock()
+
+        val notificationShadeWindowView: NotificationShadeWindowView = mock()
+        whenever(notificationShadeWindowView.resources).thenReturn(mContext.resources)
+        whenever(kosmos.notificationStackScrollLayoutController.view).thenReturn(mock())
+
+        val initController: InitController = InitController()
+
+        return StatusBarNotificationPresenter(
+                mContext,
+                shadeViewController,
+                kosmos.panelExpansionInteractor,
+                /* quickSettingsController = */ mock(),
+                kosmos.headsUpManager,
+                notificationShadeWindowView,
+                kosmos.activityStarter,
+                kosmos.notificationStackScrollLayoutController,
+                kosmos.dozeScrimController,
+                kosmos.notificationShadeWindowController,
+                kosmos.dynamicPrivacyController,
+                kosmos.keyguardStateController,
+                kosmos.notificationAlertsInteractor,
+                kosmos.lockscreenShadeTransitionController,
+                kosmos.powerInteractor,
+                commandQueue,
+                kosmos.notificationLockscreenUserManager,
+                kosmos.sysuiStatusBarStateController,
+                /* notifShadeEventSource = */ mock(),
+                /* notificationMediaManager = */ mock(),
+                /* notificationGutsManager = */ mock(),
+                initController,
+                kosmos.visualInterruptionDecisionProvider,
+                kosmos.notificationRemoteInputManager,
+                /* remoteInputManagerCallback = */ mock(),
+                /* notificationListContainer = */ mock(),
+                kosmos.deviceUnlockedInteractor,
+            )
+            .also { initController.executePostInitTasks() }
+    }
+
+    private fun verifyAndCaptureSuppressors() {
+        interruptSuppressor = null
+
+        val conditionCaptor = argumentCaptor<VisualInterruptionCondition>()
+        verify(visualInterruptionDecisionProvider, times(3)).addCondition(conditionCaptor.capture())
+        val conditions: List<VisualInterruptionCondition> = conditionCaptor.allValues
+        alertsDisabledCondition = conditions[0]
+        vrModeCondition = conditions[1]
+        panelsDisabledCondition = conditions[2]
+
+        val needsRedactionFilterCaptor = argumentCaptor<VisualInterruptionFilter>()
+        verify(visualInterruptionDecisionProvider).addFilter(needsRedactionFilterCaptor.capture())
+        needsRedactionFilter = needsRedactionFilterCaptor.lastValue
+    }
+
+    private fun verifyAndCaptureLegacySuppressor() {
+        alertsDisabledCondition = null
+        vrModeCondition = null
+        needsRedactionFilter = null
+        panelsDisabledCondition = null
+
+        val suppressorCaptor = argumentCaptor<NotificationInterruptSuppressor>()
+        verify(visualInterruptionDecisionProvider).addLegacySuppressor(suppressorCaptor.capture())
+        interruptSuppressor = suppressorCaptor.lastValue
+    }
+
+    private fun createRow(): ExpandableNotificationRow {
+        val row: ExpandableNotificationRow = mock()
+        val entry: NotificationEntry = createNotificationEntry()
+        whenever(row.entry).thenReturn(entry)
+        entry.row = row
+        return row
+    }
+
+    private fun createNotificationEntry(): NotificationEntry =
+        NotificationEntryBuilder()
+            .setPkg("a")
+            .setOpPkg("a")
+            .setTag("a")
+            .setNotification(Builder(mContext, "a").build())
+            .build()
+
+    private fun createFsiNotificationEntry(): NotificationEntry {
+        val notification: Notification =
+            Builder(mContext, "a").setFullScreenIntent(mock(), true).build()
+
+        return NotificationEntryBuilder()
+            .setPkg("a")
+            .setOpPkg("a")
+            .setTag("a")
+            .setNotification(notification)
+            .build()
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/SystemUIDialogTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/SystemUIDialogTest.java
index 79b5cc3..0652a83 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/SystemUIDialogTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/SystemUIDialogTest.java
@@ -27,6 +27,7 @@
 import static org.mockito.Mockito.when;
 
 import android.content.BroadcastReceiver;
+import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.res.Configuration;
@@ -120,6 +121,22 @@
     }
 
     @Test
+    public void testRegisterReceiverWithoutAcsd() {
+        SystemUIDialog dialog = createDialogWithDelegate(mContext, mDelegate,
+                false /* shouldAcsdDismissDialog */);
+        final ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor =
+                ArgumentCaptor.forClass(BroadcastReceiver.class);
+        final ArgumentCaptor<IntentFilter> intentFilterCaptor =
+                ArgumentCaptor.forClass(IntentFilter.class);
+
+        dialog.show();
+        verify(mBroadcastDispatcher).registerReceiver(broadcastReceiverCaptor.capture(),
+                intentFilterCaptor.capture(), ArgumentMatchers.eq(null), ArgumentMatchers.any());
+        assertTrue(intentFilterCaptor.getValue().hasAction(Intent.ACTION_SCREEN_OFF));
+        assertFalse(intentFilterCaptor.getValue().hasAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
+    }
+
+    @Test
     @RequiresFlagsEnabled(Flags.FLAG_PREDICTIVE_BACK_ANIMATE_DIALOGS)
     public void usePredictiveBackAnimFlag() {
         final SystemUIDialog dialog = new SystemUIDialog(mContext);
@@ -163,7 +180,8 @@
     public void delegateIsCalled_inCorrectOrder() {
         Configuration configuration = new Configuration();
         InOrder inOrder = Mockito.inOrder(mDelegate);
-        SystemUIDialog dialog = createDialogWithDelegate();
+        SystemUIDialog dialog = createDialogWithDelegate(mContext, mDelegate,
+                true /* shouldAcsdDismissDialog */);
 
         dialog.show();
         dialog.onWindowFocusChanged(/* hasFocus= */ true);
@@ -178,7 +196,8 @@
         inOrder.verify(mDelegate).onStop(dialog);
     }
 
-    private SystemUIDialog createDialogWithDelegate() {
+    private SystemUIDialog createDialogWithDelegate(Context context,
+            SystemUIDialog.Delegate delegate, boolean shouldAcsdDismissDialog) {
         SystemUIDialog.Factory factory = new SystemUIDialog.Factory(
                 getContext(),
                 Dependency.get(SystemUIDialogManager.class),
@@ -186,6 +205,6 @@
                 Dependency.get(BroadcastDispatcher.class),
                 Dependency.get(DialogTransitionAnimator.class)
         );
-        return factory.create(mDelegate);
+        return factory.create(delegate, context, shouldAcsdDismissDialog);
     }
 }
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
index c90183d..1cc55bf 100644
--- 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
@@ -127,12 +127,6 @@
         kosmos.configureKeyguardBypass(isBypassAvailable = skipIsBypassAvailableCheck)
         underTest = kosmos.keyguardBypassInteractor
 
-        // bouncerShowing false, !onLockscreenScene false
-        // !onLockscreenScene false
-        setScene(
-            bouncerShowing = !skipBouncerShowingCheck,
-            onLockscreenScene = skipOnLockscreenSceneCheck,
-        )
         // alternateBouncerShowing false
         setAlternateBouncerShowing(!skipAlternateBouncerShowingCheck)
         // launchingAffordance false
@@ -141,6 +135,13 @@
         setPulseExpanding(!skipPulseExpandingCheck)
         // qsExpanding false
         setQsExpanded(!skipQsExpandedCheck)
+
+        // bouncerShowing false, !onLockscreenScene false
+        // !onLockscreenScene false
+        setScene(
+            bouncerShowing = !skipBouncerShowingCheck,
+            onLockscreenScene = skipOnLockscreenSceneCheck,
+        )
     }
 
     private fun setAlternateBouncerShowing(alternateBouncerVisible: Boolean) {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractorTest.kt
index ca1413e..8fb95e8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractorTest.kt
@@ -30,17 +30,25 @@
 import com.android.systemui.kosmos.runTest
 import com.android.systemui.kosmos.useUnconfinedTestDispatcher
 import com.android.systemui.statusbar.StatusBarIconView
+import com.android.systemui.statusbar.data.repository.fakeStatusBarModeRepository
+import com.android.systemui.statusbar.gesture.swipeStatusBarAwayGestureHandler
 import com.android.systemui.statusbar.notification.data.model.activeNotificationModel
 import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore
 import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository
 import com.android.systemui.statusbar.notification.shared.CallType
 import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
+import com.android.systemui.statusbar.window.fakeStatusBarWindowControllerStore
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.runTest
+import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.clearInvocations
 import org.mockito.kotlin.mock
+import org.mockito.kotlin.never
+import org.mockito.kotlin.verify
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
@@ -49,6 +57,11 @@
     private val repository = kosmos.activeNotificationListRepository
     private val underTest = kosmos.ongoingCallInteractor
 
+    @Before
+    fun setUp() {
+        underTest.start()
+    }
+
     @Test
     fun noNotification_emitsNoCall() = runTest {
         val state by collectLastValue(underTest.ongoingCallState)
@@ -72,7 +85,7 @@
                                 whenTime = 1000L,
                                 callType = CallType.Ongoing,
                                 statusBarChipIcon = testIconView,
-                                contentIntent = testIntent
+                                contentIntent = testIntent,
                             )
                         )
                     }
@@ -95,7 +108,9 @@
                     .apply {
                         addIndividualNotif(
                             activeNotificationModel(
-                                key = "notif1", whenTime = 1000L, callType = CallType.Ongoing
+                                key = "notif1",
+                                whenTime = 1000L,
+                                callType = CallType.Ongoing,
                             )
                         )
                     }
@@ -114,7 +129,9 @@
                     .apply {
                         addIndividualNotif(
                             activeNotificationModel(
-                                key = "notif1", whenTime = 1000L, callType = CallType.Ongoing
+                                key = "notif1",
+                                whenTime = 1000L,
+                                callType = CallType.Ongoing,
                             )
                         )
                     }
@@ -138,7 +155,7 @@
                                 key = "notif1",
                                 whenTime = 1000L,
                                 callType = CallType.Ongoing,
-                                uid = UID
+                                uid = UID,
                             )
                         )
                     }
@@ -161,7 +178,7 @@
                                 key = "notif1",
                                 whenTime = 1000L,
                                 callType = CallType.Ongoing,
-                                uid = UID
+                                uid = UID,
                             )
                         )
                     }
@@ -185,13 +202,12 @@
                                 key = "notif1",
                                 whenTime = 1000L,
                                 callType = CallType.Ongoing,
-                                uid = UID
+                                uid = UID,
                             )
                         )
                     }
                     .build()
-            assertThat(latest)
-                .isInstanceOf(OngoingCallModel.InCall::class.java)
+            assertThat(latest).isInstanceOf(OngoingCallModel.InCall::class.java)
 
             // App becomes visible
             kosmos.activityManagerRepository.fake.setIsAppVisible(UID, true)
@@ -202,6 +218,176 @@
             assertThat(latest).isInstanceOf(OngoingCallModel.InCall::class.java)
         }
 
+    @Test
+    fun ongoingCallNotification_setsRequiresStatusBarVisibleTrue() =
+        kosmos.runTest {
+            val isStatusBarRequired by collectLastValue(underTest.isStatusBarRequiredForOngoingCall)
+            val requiresStatusBarVisibleInRepository by
+                collectLastValue(
+                    kosmos.fakeStatusBarModeRepository.defaultDisplay
+                        .ongoingProcessRequiresStatusBarVisible
+                )
+            val requiresStatusBarVisibleInWindowController by
+                collectLastValue(
+                    kosmos.fakeStatusBarWindowControllerStore.defaultDisplay
+                        .ongoingProcessRequiresStatusBarVisible
+                )
+            postOngoingCallNotification()
+
+            assertThat(isStatusBarRequired).isTrue()
+            assertThat(requiresStatusBarVisibleInRepository).isTrue()
+            assertThat(requiresStatusBarVisibleInWindowController).isTrue()
+        }
+
+    @Test
+    fun notificationRemoved_setsRequiresStatusBarVisibleFalse() =
+        kosmos.runTest {
+            val isStatusBarRequired by collectLastValue(underTest.isStatusBarRequiredForOngoingCall)
+            val requiresStatusBarVisibleInRepository by
+                collectLastValue(
+                    kosmos.fakeStatusBarModeRepository.defaultDisplay
+                        .ongoingProcessRequiresStatusBarVisible
+                )
+            val requiresStatusBarVisibleInWindowController by
+                collectLastValue(
+                    kosmos.fakeStatusBarWindowControllerStore.defaultDisplay
+                        .ongoingProcessRequiresStatusBarVisible
+                )
+
+            postOngoingCallNotification()
+
+            repository.activeNotifications.value = ActiveNotificationsStore()
+
+            assertThat(isStatusBarRequired).isFalse()
+            assertThat(requiresStatusBarVisibleInRepository).isFalse()
+            assertThat(requiresStatusBarVisibleInWindowController).isFalse()
+        }
+
+    @Test
+    fun ongoingCallNotification_appBecomesVisible_setsRequiresStatusBarVisibleFalse() =
+        kosmos.runTest {
+            val ongoingCallState by collectLastValue(underTest.ongoingCallState)
+
+            val requiresStatusBarVisibleInRepository by
+                collectLastValue(
+                    kosmos.fakeStatusBarModeRepository.defaultDisplay
+                        .ongoingProcessRequiresStatusBarVisible
+                )
+            val requiresStatusBarVisibleInWindowController by
+                collectLastValue(
+                    kosmos.fakeStatusBarWindowControllerStore.defaultDisplay
+                        .ongoingProcessRequiresStatusBarVisible
+                )
+
+            kosmos.activityManagerRepository.fake.startingIsAppVisibleValue = false
+
+            postOngoingCallNotification()
+
+            assertThat(ongoingCallState).isInstanceOf(OngoingCallModel.InCall::class.java)
+            assertThat(requiresStatusBarVisibleInRepository).isTrue()
+            assertThat(requiresStatusBarVisibleInWindowController).isTrue()
+
+            kosmos.activityManagerRepository.fake.setIsAppVisible(UID, true)
+
+            assertThat(ongoingCallState)
+                .isInstanceOf(OngoingCallModel.InCallWithVisibleApp::class.java)
+            assertThat(requiresStatusBarVisibleInRepository).isFalse()
+            assertThat(requiresStatusBarVisibleInWindowController).isFalse()
+        }
+
+    @Test
+    fun gestureHandler_inCall_notFullscreen_doesNotListen() =
+        kosmos.runTest {
+            val ongoingCallState by collectLastValue(underTest.ongoingCallState)
+
+            clearInvocations(kosmos.swipeStatusBarAwayGestureHandler)
+            // Set up notification but not in fullscreen
+            kosmos.fakeStatusBarModeRepository.defaultDisplay.isInFullscreenMode.value = false
+            postOngoingCallNotification()
+
+            assertThat(ongoingCallState).isInstanceOf(OngoingCallModel.InCall::class.java)
+            verify(kosmos.swipeStatusBarAwayGestureHandler, never())
+                .addOnGestureDetectedCallback(any(), any())
+        }
+
+    @Test
+    fun gestureHandler_inCall_fullscreen_addsListener() =
+        kosmos.runTest {
+            val isGestureListeningEnabled by collectLastValue(underTest.isGestureListeningEnabled)
+
+            // Set up notification and fullscreen mode
+            kosmos.fakeStatusBarModeRepository.defaultDisplay.isInFullscreenMode.value = true
+            postOngoingCallNotification()
+
+            assertThat(isGestureListeningEnabled).isTrue()
+            verify(kosmos.swipeStatusBarAwayGestureHandler)
+                .addOnGestureDetectedCallback(any(), any())
+        }
+
+    @Test
+    fun gestureHandler_inCall_fullscreen_chipSwiped_removesListener() =
+        kosmos.runTest {
+            val swipeAwayState by collectLastValue(underTest.isChipSwipedAway)
+
+            // Set up notification and fullscreen mode
+            kosmos.fakeStatusBarModeRepository.defaultDisplay.isInFullscreenMode.value = true
+            postOngoingCallNotification()
+
+            clearInvocations(kosmos.swipeStatusBarAwayGestureHandler)
+
+            underTest.onStatusBarSwiped()
+
+            assertThat(swipeAwayState).isTrue()
+            verify(kosmos.swipeStatusBarAwayGestureHandler).removeOnGestureDetectedCallback(any())
+        }
+
+    @Test
+    fun chipSwipedAway_setsRequiresStatusBarVisibleFalse() =
+        kosmos.runTest {
+            val isStatusBarRequiredForOngoingCall by
+                collectLastValue(underTest.isStatusBarRequiredForOngoingCall)
+            val requiresStatusBarVisibleInRepository by
+                collectLastValue(
+                    kosmos.fakeStatusBarModeRepository.defaultDisplay
+                        .ongoingProcessRequiresStatusBarVisible
+                )
+            val requiresStatusBarVisibleInWindowController by
+                collectLastValue(
+                    kosmos.fakeStatusBarWindowControllerStore.defaultDisplay
+                        .ongoingProcessRequiresStatusBarVisible
+                )
+
+            // Start with an ongoing call (which should set status bar required)
+            postOngoingCallNotification()
+
+            assertThat(isStatusBarRequiredForOngoingCall).isTrue()
+            assertThat(requiresStatusBarVisibleInRepository).isTrue()
+            assertThat(requiresStatusBarVisibleInWindowController).isTrue()
+
+            // Swipe away the chip
+            underTest.onStatusBarSwiped()
+
+            // Verify status bar is no longer required
+            assertThat(requiresStatusBarVisibleInRepository).isFalse()
+            assertThat(requiresStatusBarVisibleInWindowController).isFalse()
+        }
+
+    private fun postOngoingCallNotification() {
+        repository.activeNotifications.value =
+            ActiveNotificationsStore.Builder()
+                .apply {
+                    addIndividualNotif(
+                        activeNotificationModel(
+                            key = "notif1",
+                            whenTime = 1000L,
+                            callType = CallType.Ongoing,
+                            uid = UID,
+                        )
+                    )
+                }
+                .build()
+    }
+
     companion object {
         private const val UID = 885
     }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt
index 038722c..bf1fbad 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt
@@ -20,8 +20,6 @@
 import android.platform.test.annotations.EnableFlags
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.settingslib.AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH
-import com.android.settingslib.AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH_NONE
 import com.android.settingslib.mobile.MobileMappings
 import com.android.settingslib.mobile.TelephonyIcons.G
 import com.android.settingslib.mobile.TelephonyIcons.THREE_G
@@ -40,12 +38,15 @@
 import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
 import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
 import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionState
+import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionRepository
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionRepository.Companion.DEFAULT_NETWORK_NAME
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository
 import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconInteractorImpl
 import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractorImpl
 import com.android.systemui.statusbar.pipeline.mobile.domain.model.SignalIconModel
+import com.android.systemui.statusbar.pipeline.mobile.ui.model.MobileContentDescription
 import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
 import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
 import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot
@@ -255,59 +256,146 @@
     @Test
     fun contentDescription_notInService_usesNoPhone() =
         testScope.runTest {
-            var latest: ContentDescription? = null
-            val job = underTest.contentDescription.onEach { latest = it }.launchIn(this)
+            val latest by collectLastValue(underTest.contentDescription)
 
             repository.isInService.value = false
 
-            assertThat((latest as ContentDescription.Resource).res)
-                .isEqualTo(PHONE_SIGNAL_STRENGTH_NONE)
+            assertThat(latest as MobileContentDescription.Cellular)
+                .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, NO_SIGNAL))
+        }
 
-            job.cancel()
+    @Test
+    fun contentDescription_includesNetworkName() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.contentDescription)
+
+            repository.isInService.value = true
+            repository.networkName.value = NetworkNameModel.SubscriptionDerived("Test Network Name")
+            repository.numberOfLevels.value = 5
+            repository.setAllLevels(3)
+
+            assertThat(latest as MobileContentDescription.Cellular)
+                .isEqualTo(MobileContentDescription.Cellular("Test Network Name", THREE_BARS))
         }
 
     @Test
     fun contentDescription_inService_usesLevel() =
         testScope.runTest {
-            var latest: ContentDescription? = null
-            val job = underTest.contentDescription.onEach { latest = it }.launchIn(this)
+            val latest by collectLastValue(underTest.contentDescription)
 
             repository.setAllLevels(2)
-            assertThat((latest as ContentDescription.Resource).res)
-                .isEqualTo(PHONE_SIGNAL_STRENGTH[2])
+
+            assertThat(latest as MobileContentDescription.Cellular)
+                .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, TWO_BARS))
 
             repository.setAllLevels(0)
-            assertThat((latest as ContentDescription.Resource).res)
-                .isEqualTo(PHONE_SIGNAL_STRENGTH[0])
 
-            job.cancel()
+            assertThat(latest as MobileContentDescription.Cellular)
+                .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, NO_SIGNAL))
         }
 
     @Test
-    fun contentDescription_nonInflated_invalidLevelIsNull() =
+    fun contentDescription_nonInflated_invalidLevelUsesNoSignalText() =
         testScope.runTest {
             val latest by collectLastValue(underTest.contentDescription)
 
             repository.inflateSignalStrength.value = false
             repository.setAllLevels(-1)
-            assertThat(latest).isNull()
+
+            assertThat(latest as MobileContentDescription.Cellular)
+                .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, NO_SIGNAL))
 
             repository.setAllLevels(100)
-            assertThat(latest).isNull()
+
+            assertThat(latest as MobileContentDescription.Cellular)
+                .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, NO_SIGNAL))
         }
 
     @Test
-    fun contentDescription_inflated_invalidLevelIsNull() =
+    fun contentDescription_nonInflated_levelStrings() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.contentDescription)
+
+            repository.inflateSignalStrength.value = false
+            repository.setAllLevels(0)
+
+            assertThat(latest as MobileContentDescription.Cellular)
+                .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, NO_SIGNAL))
+
+            repository.setAllLevels(1)
+
+            assertThat(latest as MobileContentDescription.Cellular)
+                .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, ONE_BAR))
+
+            repository.setAllLevels(2)
+
+            assertThat(latest as MobileContentDescription.Cellular)
+                .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, TWO_BARS))
+
+            repository.setAllLevels(3)
+
+            assertThat(latest as MobileContentDescription.Cellular)
+                .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, THREE_BARS))
+
+            repository.setAllLevels(4)
+
+            assertThat(latest as MobileContentDescription.Cellular)
+                .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, FULL_BARS))
+        }
+
+    @Test
+    fun contentDescription_inflated_invalidLevelUsesNoSignalText() =
         testScope.runTest {
             val latest by collectLastValue(underTest.contentDescription)
 
             repository.inflateSignalStrength.value = true
             repository.numberOfLevels.value = 6
+
             repository.setAllLevels(-2)
-            assertThat(latest).isNull()
+
+            assertThat(latest as MobileContentDescription.Cellular)
+                .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, NO_SIGNAL))
 
             repository.setAllLevels(100)
-            assertThat(latest).isNull()
+
+            assertThat(latest as MobileContentDescription.Cellular)
+                .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, NO_SIGNAL))
+        }
+
+    @Test
+    fun contentDescription_inflated_levelStrings() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.contentDescription)
+
+            repository.inflateSignalStrength.value = true
+            repository.numberOfLevels.value = 6
+
+            // Note that the _repo_ level is 1 lower than the reported level through the interactor
+
+            repository.setAllLevels(0)
+
+            assertThat(latest as MobileContentDescription.Cellular)
+                .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, ONE_BAR))
+
+            repository.setAllLevels(1)
+
+            assertThat(latest as MobileContentDescription.Cellular)
+                .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, TWO_BARS))
+
+            repository.setAllLevels(2)
+
+            assertThat(latest as MobileContentDescription.Cellular)
+                .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, THREE_BARS))
+
+            repository.setAllLevels(3)
+
+            assertThat(latest as MobileContentDescription.Cellular)
+                .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, FOUR_BARS))
+
+            repository.setAllLevels(4)
+
+            assertThat(latest as MobileContentDescription.Cellular)
+                .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, FULL_BARS))
         }
 
     @Test
@@ -323,7 +411,10 @@
                 repository.setAllLevels(i)
                 when (i) {
                     -1,
-                    5 -> assertWithMessage("Level $i is expected to be null").that(latest).isNull()
+                    5 ->
+                        assertWithMessage("Level $i is expected to be 'no signal'")
+                            .that((latest as MobileContentDescription.Cellular).levelDescriptionRes)
+                            .isEqualTo(NO_SIGNAL)
                     else ->
                         assertWithMessage("Level $i is expected not to be null")
                             .that(latest)
@@ -344,7 +435,10 @@
                 repository.setAllLevels(i)
                 when (i) {
                     -2,
-                    5 -> assertWithMessage("Level $i is expected to be null").that(latest).isNull()
+                    5 ->
+                        assertWithMessage("Level $i is expected to be 'no signal'")
+                            .that((latest as MobileContentDescription.Cellular).levelDescriptionRes)
+                            .isEqualTo(NO_SIGNAL)
                     else ->
                         assertWithMessage("Level $i is not expected to be null")
                             .that(latest)
@@ -967,5 +1061,13 @@
 
     companion object {
         private const val SUB_1_ID = 1
+
+        // For convenience, just define these as constants
+        private val NO_SIGNAL = R.string.accessibility_no_signal
+        private val ONE_BAR = R.string.accessibility_one_bar
+        private val TWO_BARS = R.string.accessibility_two_bars
+        private val THREE_BARS = R.string.accessibility_three_bars
+        private val FOUR_BARS = R.string.accessibility_four_bars
+        private val FULL_BARS = R.string.accessibility_signal_full
     }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelImplTest.kt
index 7b04fc8..5c1141b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelImplTest.kt
@@ -67,8 +67,9 @@
 import com.android.systemui.statusbar.events.shared.model.SystemEventAnimationState.Idle
 import com.android.systemui.statusbar.notification.data.model.activeNotificationModel
 import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore
-import com.android.systemui.statusbar.notification.data.repository.FakeHeadsUpRowRepository
+import com.android.systemui.statusbar.notification.data.repository.UnconfinedFakeHeadsUpRowRepository
 import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository
+import com.android.systemui.statusbar.notification.headsup.PinnedStatus
 import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel
 import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor
 import com.android.systemui.statusbar.notification.stack.data.repository.headsUpNotificationRepository
@@ -76,6 +77,7 @@
 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.emptyFlow
 import kotlinx.coroutines.test.UnconfinedTestDispatcher
 import org.junit.Before
@@ -518,7 +520,8 @@
         }
 
     @Test
-    fun isClockVisible_allowedByFlags_hunActive_notVisible() =
+    @EnableFlags(StatusBarNotifChips.FLAG_NAME)
+    fun isClockVisible_allowedByFlags_hunPinnedByUser_visible() =
         kosmos.runTest {
             val latest by collectLastValue(underTest.isClockVisible)
             transitionKeyguardToGone()
@@ -527,7 +530,29 @@
                 DisableFlagsModel(DISABLE_NONE, DISABLE2_NONE)
             // there is an active HUN
             headsUpNotificationRepository.setNotifications(
-                fakeHeadsUpRowRepository(isPinned = true)
+                UnconfinedFakeHeadsUpRowRepository(
+                    key = "key",
+                    pinnedStatus = MutableStateFlow(PinnedStatus.PinnedByUser),
+                )
+            )
+
+            assertThat(latest!!.visibility).isEqualTo(View.VISIBLE)
+        }
+
+    @Test
+    fun isClockVisible_allowedByFlags_hunPinnedBySystem_notVisible() =
+        kosmos.runTest {
+            val latest by collectLastValue(underTest.isClockVisible)
+            transitionKeyguardToGone()
+
+            fakeDisableFlagsRepository.disableFlags.value =
+                DisableFlagsModel(DISABLE_NONE, DISABLE2_NONE)
+            // there is an active HUN
+            headsUpNotificationRepository.setNotifications(
+                UnconfinedFakeHeadsUpRowRepository(
+                    key = "key",
+                    pinnedStatus = MutableStateFlow(PinnedStatus.PinnedBySystem),
+                )
             )
 
             assertThat(latest!!.visibility).isEqualTo(View.INVISIBLE)
@@ -541,10 +566,14 @@
 
             fakeDisableFlagsRepository.disableFlags.value =
                 DisableFlagsModel(DISABLE_NONE, DISABLE2_NONE)
-            // there is an active HUN
+            // there is an active HUN pinned by the system
             headsUpNotificationRepository.setNotifications(
-                fakeHeadsUpRowRepository(isPinned = true)
+                UnconfinedFakeHeadsUpRowRepository(
+                    key = "key",
+                    pinnedStatus = MutableStateFlow(PinnedStatus.PinnedBySystem),
+                )
             )
+            assertThat(latest!!.visibility).isEqualTo(View.INVISIBLE)
 
             // hun goes away
             headsUpNotificationRepository.setNotifications(listOf())
@@ -562,7 +591,10 @@
                 DisableFlagsModel(DISABLE_CLOCK, DISABLE2_NONE)
             // there is an active HUN
             headsUpNotificationRepository.setNotifications(
-                fakeHeadsUpRowRepository(isPinned = true)
+                UnconfinedFakeHeadsUpRowRepository(
+                    key = "key",
+                    pinnedStatus = MutableStateFlow(PinnedStatus.PinnedBySystem),
+                )
             )
 
             assertThat(latest!!.visibility).isEqualTo(View.INVISIBLE)
@@ -898,10 +930,6 @@
             assertThat(systemInfoVisible!!.baseVisibility.visibility).isEqualTo(View.GONE)
         }
 
-    // Cribbed from [HeadsUpNotificationInteractorTest.kt]
-    private fun fakeHeadsUpRowRepository(key: String = "test key", isPinned: Boolean = false) =
-        FakeHeadsUpRowRepository(key = key, isPinned = isPinned)
-
     private fun activeNotificationsStore(notifications: List<ActiveNotificationModel>) =
         ActiveNotificationsStore.Builder()
             .apply { notifications.forEach(::addIndividualNotif) }
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 1b7b47f..2eac39e 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
@@ -66,7 +66,7 @@
             doAnswer {
                     // See Utils.isHeadlessRemoteDisplayProvider
                     if ((it.arguments[0] as Intent).`package` == HEADLESS_REMOTE_PACKAGE) {
-                        emptyList()
+                        emptyList<ResolveInfo>()
                     } else {
                         listOf(mock<ResolveInfo>())
                     }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/StateTransitionsTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/StateTransitionsTest.kt
new file mode 100644
index 0000000..2ad1124
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/StateTransitionsTest.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.touchpad.tutorial.ui
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState
+import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.Error
+import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.Finished
+import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.InProgress
+import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.InProgressAfterError
+import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.NotStarted
+import com.android.systemui.touchpad.tutorial.ui.composable.toGestureUiState
+import com.android.systemui.touchpad.tutorial.ui.composable.toTutorialActionState
+import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class StateTransitionsTest : SysuiTestCase() {
+
+    companion object {
+        private const val START_MARKER = "startMarker"
+        private const val END_MARKER = "endMarker"
+        private const val SUCCESS_ANIMATION = 0
+    }
+
+    // needed to simulate caching last state as it's used to create new state
+    private var lastState: TutorialActionState = NotStarted
+
+    private fun GestureState.toTutorialActionState(): TutorialActionState {
+        val newState =
+            this.toGestureUiState(
+                    progressStartMarker = START_MARKER,
+                    progressEndMarker = END_MARKER,
+                    successAnimation = SUCCESS_ANIMATION,
+                )
+                .toTutorialActionState(lastState)
+        lastState = newState
+        return lastState
+    }
+
+    @Test
+    fun gestureStateProducesEquivalentTutorialActionStateInHappyPath() {
+        val happyPath =
+            listOf(
+                GestureState.NotStarted,
+                GestureState.InProgress(0f),
+                GestureState.InProgress(0.5f),
+                GestureState.InProgress(1f),
+                GestureState.Finished,
+            )
+
+        val resultingStates = mutableListOf<TutorialActionState>()
+        happyPath.forEach { resultingStates.add(it.toTutorialActionState()) }
+
+        assertThat(resultingStates)
+            .containsExactly(
+                NotStarted,
+                InProgress(0f, START_MARKER, END_MARKER),
+                InProgress(0.5f, START_MARKER, END_MARKER),
+                InProgress(1f, START_MARKER, END_MARKER),
+                Finished(SUCCESS_ANIMATION),
+            )
+            .inOrder()
+    }
+
+    @Test
+    fun gestureStateProducesEquivalentTutorialActionStateInErrorPath() {
+        val errorPath =
+            listOf(
+                GestureState.NotStarted,
+                GestureState.InProgress(0f),
+                GestureState.Error,
+                GestureState.InProgress(0.5f),
+                GestureState.InProgress(1f),
+                GestureState.Finished,
+            )
+
+        val resultingStates = mutableListOf<TutorialActionState>()
+        errorPath.forEach { resultingStates.add(it.toTutorialActionState()) }
+
+        assertThat(resultingStates)
+            .containsExactly(
+                NotStarted,
+                InProgress(0f, START_MARKER, END_MARKER),
+                Error,
+                InProgressAfterError(InProgress(0.5f, START_MARKER, END_MARKER)),
+                InProgressAfterError(InProgress(1f, START_MARKER, END_MARKER)),
+                Finished(SUCCESS_ANIMATION),
+            )
+            .inOrder()
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureRecognizerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureRecognizerTest.kt
index d9d8169..2f3f75f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureRecognizerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureRecognizerTest.kt
@@ -22,7 +22,6 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.touchpad.tutorial.ui.gesture.GestureDirection.LEFT
 import com.android.systemui.touchpad.tutorial.ui.gesture.GestureDirection.RIGHT
-import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.Finished
 import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.InProgress
 import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.NotStarted
 import com.android.systemui.touchpad.tutorial.ui.gesture.MultiFingerGesture.Companion.SWIPE_DISTANCE
@@ -45,24 +44,6 @@
     }
 
     @Test
-    fun triggersGestureFinishedForThreeFingerGestureRight() {
-        assertStateAfterEvents(events = ThreeFingerGesture.swipeRight(), expectedState = Finished)
-    }
-
-    @Test
-    fun triggersGestureFinishedForThreeFingerGestureLeft() {
-        assertStateAfterEvents(events = ThreeFingerGesture.swipeLeft(), expectedState = Finished)
-    }
-
-    @Test
-    fun triggersGestureProgressForThreeFingerGestureStarted() {
-        assertStateAfterEvents(
-            events = ThreeFingerGesture.startEvents(x = 0f, y = 0f),
-            expectedState = InProgress(),
-        )
-    }
-
-    @Test
     fun triggersProgressRelativeToDistanceWhenSwipingLeft() {
         assertProgressWhileMovingFingers(
             deltaX = -SWIPE_DISTANCE / 2,
@@ -86,13 +67,6 @@
         )
     }
 
-    private fun assertProgressWhileMovingFingers(deltaX: Float, expected: InProgress) {
-        assertStateAfterEvents(
-            events = ThreeFingerGesture.eventsForGestureInProgress { move(deltaX = deltaX) },
-            expectedState = expected,
-        )
-    }
-
     @Test
     fun triggeredProgressIsNoBiggerThanOne() {
         assertProgressWhileMovingFingers(
@@ -105,30 +79,13 @@
         )
     }
 
-    @Test
-    fun doesntTriggerGestureFinished_onGestureDistanceTooShort() {
+    private fun assertProgressWhileMovingFingers(deltaX: Float, expected: InProgress) {
         assertStateAfterEvents(
-            events = ThreeFingerGesture.swipeLeft(distancePx = SWIPE_DISTANCE / 2),
-            expectedState = NotStarted,
+            events = ThreeFingerGesture.eventsForGestureInProgress { move(deltaX = deltaX) },
+            expectedState = expected,
         )
     }
 
-    @Test
-    fun doesntTriggerGestureFinished_onThreeFingersSwipeInOtherDirections() {
-        assertStateAfterEvents(events = ThreeFingerGesture.swipeUp(), expectedState = NotStarted)
-        assertStateAfterEvents(events = ThreeFingerGesture.swipeDown(), expectedState = NotStarted)
-    }
-
-    @Test
-    fun doesntTriggerGestureFinished_onTwoFingersSwipe() {
-        assertStateAfterEvents(events = TwoFingerGesture.swipeRight(), expectedState = NotStarted)
-    }
-
-    @Test
-    fun doesntTriggerGestureFinished_onFourFingersSwipe() {
-        assertStateAfterEvents(events = FourFingerGesture.swipeRight(), expectedState = NotStarted)
-    }
-
     private fun assertStateAfterEvents(events: List<MotionEvent>, expectedState: GestureState) {
         events.forEach { gestureRecognizer.accept(it) }
         assertThat(gestureState).isEqualTo(expectedState)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureRecognizerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureRecognizerTest.kt
index 7aa389a..e23348b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureRecognizerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureRecognizerTest.kt
@@ -21,7 +21,7 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.testKosmos
-import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.Finished
+import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.Error
 import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.InProgress
 import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.NotStarted
 import com.android.systemui.touchpad.tutorial.ui.gesture.MultiFingerGesture.Companion.SWIPE_DISTANCE
@@ -57,22 +57,9 @@
     }
 
     @Test
-    fun triggersGestureFinishedForThreeFingerGestureUp() {
-        assertStateAfterEvents(events = ThreeFingerGesture.swipeUp(), expectedState = Finished)
-    }
-
-    @Test
-    fun doesntTriggerGestureFinished_onGestureSpeedTooSlow() {
+    fun triggersError_onGestureSpeedTooSlow() {
         velocityTracker.setVelocity(Velocity(SLOW))
-        assertStateAfterEvents(events = ThreeFingerGesture.swipeUp(), expectedState = NotStarted)
-    }
-
-    @Test
-    fun triggersGestureProgressForThreeFingerGestureStarted() {
-        assertStateAfterEvents(
-            events = ThreeFingerGesture.startEvents(x = 0f, y = 0f),
-            expectedState = InProgress(),
-        )
+        assertStateAfterEvents(events = ThreeFingerGesture.swipeUp(), expectedState = Error)
     }
 
     @Test
@@ -81,13 +68,6 @@
         assertProgressWhileMovingFingers(deltaY = -SWIPE_DISTANCE, expectedProgress = 1f)
     }
 
-    private fun assertProgressWhileMovingFingers(deltaY: Float, expectedProgress: Float) {
-        assertStateAfterEvents(
-            events = ThreeFingerGesture.eventsForGestureInProgress { move(deltaY = deltaY) },
-            expectedState = InProgress(progress = expectedProgress),
-        )
-    }
-
     @Test
     fun triggeredProgressIsBetweenZeroAndOne() {
         // going in the wrong direction
@@ -96,31 +76,13 @@
         assertProgressWhileMovingFingers(deltaY = -SWIPE_DISTANCE * 2, expectedProgress = 1f)
     }
 
-    @Test
-    fun doesntTriggerGestureFinished_onGestureDistanceTooShort() {
+    private fun assertProgressWhileMovingFingers(deltaY: Float, expectedProgress: Float) {
         assertStateAfterEvents(
-            events = ThreeFingerGesture.swipeUp(distancePx = SWIPE_DISTANCE / 2),
-            expectedState = NotStarted,
+            events = ThreeFingerGesture.eventsForGestureInProgress { move(deltaY = deltaY) },
+            expectedState = InProgress(progress = expectedProgress),
         )
     }
 
-    @Test
-    fun doesntTriggerGestureFinished_onThreeFingersSwipeInOtherDirections() {
-        assertStateAfterEvents(events = ThreeFingerGesture.swipeDown(), expectedState = NotStarted)
-        assertStateAfterEvents(events = ThreeFingerGesture.swipeLeft(), expectedState = NotStarted)
-        assertStateAfterEvents(events = ThreeFingerGesture.swipeRight(), expectedState = NotStarted)
-    }
-
-    @Test
-    fun doesntTriggerGestureFinished_onTwoFingersSwipe() {
-        assertStateAfterEvents(events = TwoFingerGesture.swipeUp(), expectedState = NotStarted)
-    }
-
-    @Test
-    fun doesntTriggerGestureFinished_onFourFingersSwipe() {
-        assertStateAfterEvents(events = FourFingerGesture.swipeUp(), expectedState = NotStarted)
-    }
-
     private fun assertStateAfterEvents(events: List<MotionEvent>, expectedState: GestureState) {
         events.forEach { gestureRecognizer.accept(it) }
         assertThat(gestureState).isEqualTo(expectedState)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureRecognizerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureRecognizerTest.kt
index cb74e65..2fe37ae 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureRecognizerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureRecognizerTest.kt
@@ -21,7 +21,7 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.testKosmos
-import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.Finished
+import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.Error
 import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.InProgress
 import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.NotStarted
 import com.android.systemui.touchpad.tutorial.ui.gesture.MultiFingerGesture.Companion.SWIPE_DISTANCE
@@ -58,22 +58,9 @@
     }
 
     @Test
-    fun triggersGestureFinishedForThreeFingerGestureUp() {
-        assertStateAfterEvents(events = ThreeFingerGesture.swipeUp(), expectedState = Finished)
-    }
-
-    @Test
-    fun doesntTriggerGestureFinished_onGestureSpeedTooHigh() {
+    fun triggersError_onGestureSpeedTooHigh() {
         velocityTracker.setVelocity(Velocity(FAST))
-        assertStateAfterEvents(events = ThreeFingerGesture.swipeUp(), expectedState = NotStarted)
-    }
-
-    @Test
-    fun triggersGestureProgressForThreeFingerGestureStarted() {
-        assertStateAfterEvents(
-            events = ThreeFingerGesture.startEvents(x = 0f, y = 0f),
-            expectedState = InProgress(progress = 0f),
-        )
+        assertStateAfterEvents(events = ThreeFingerGesture.swipeUp(), expectedState = Error)
     }
 
     @Test
@@ -97,31 +84,6 @@
         assertProgressWhileMovingFingers(deltaY = -SWIPE_DISTANCE * 2, expectedProgress = 1f)
     }
 
-    @Test
-    fun doesntTriggerGestureFinished_onGestureDistanceTooShort() {
-        assertStateAfterEvents(
-            events = ThreeFingerGesture.swipeUp(distancePx = SWIPE_DISTANCE / 2),
-            expectedState = NotStarted,
-        )
-    }
-
-    @Test
-    fun doesntTriggerGestureFinished_onThreeFingersSwipeInOtherDirections() {
-        assertStateAfterEvents(events = ThreeFingerGesture.swipeDown(), expectedState = NotStarted)
-        assertStateAfterEvents(events = ThreeFingerGesture.swipeLeft(), expectedState = NotStarted)
-        assertStateAfterEvents(events = ThreeFingerGesture.swipeRight(), expectedState = NotStarted)
-    }
-
-    @Test
-    fun doesntTriggerGestureFinished_onTwoFingersSwipe() {
-        assertStateAfterEvents(events = TwoFingerGesture.swipeUp(), expectedState = NotStarted)
-    }
-
-    @Test
-    fun doesntTriggerGestureFinished_onFourFingersSwipe() {
-        assertStateAfterEvents(events = FourFingerGesture.swipeUp(), expectedState = NotStarted)
-    }
-
     private fun assertStateAfterEvents(events: List<MotionEvent>, expectedState: GestureState) {
         events.forEach { gestureRecognizer.accept(it) }
         assertThat(gestureState).isEqualTo(expectedState)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/ThreeFingerGestureRecognizerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/ThreeFingerGestureRecognizerTest.kt
new file mode 100644
index 0000000..8972f3e
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/ThreeFingerGestureRecognizerTest.kt
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.touchpad.tutorial.ui.gesture
+
+import android.view.MotionEvent
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.Error
+import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.Finished
+import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.InProgress
+import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.NotStarted
+import com.android.systemui.touchpad.tutorial.ui.gesture.MultiFingerGesture.Companion.SWIPE_DISTANCE
+import com.android.systemui.touchpad.tutorial.ui.gesture.RecentAppsGestureRecognizerTest.Companion.FAST
+import com.android.systemui.touchpad.tutorial.ui.gesture.RecentAppsGestureRecognizerTest.Companion.SLOW
+import com.android.systemui.touchpad.tutorial.ui.gesture.RecentAppsGestureRecognizerTest.Companion.THRESHOLD_VELOCITY_PX_PER_MS
+import com.android.systemui.touchpad.ui.gesture.FakeVelocityTracker
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
+
+@SmallTest
+@RunWith(ParameterizedAndroidJunit4::class)
+class ThreeFingerGestureRecognizerTest(
+    private val recognizer: GestureRecognizer,
+    private val validGestures: Set<List<MotionEvent>>,
+    private val tooShortGesture: List<MotionEvent>,
+    @Suppress("UNUSED_PARAMETER") testSuffix: String, // here just for nicer test names
+) : SysuiTestCase() {
+
+    private var gestureState: GestureState = GestureState.NotStarted
+
+    @Before
+    fun before() {
+        recognizer.addGestureStateCallback { gestureState = it }
+    }
+
+    @Test
+    fun triggersGestureFinishedForValidGestures() {
+        validGestures.forEach { assertStateAfterEvents(events = it, expectedState = Finished) }
+    }
+
+    @Test
+    fun triggersGestureProgressForThreeFingerGestureStarted() {
+        assertStateAfterEvents(
+            events = ThreeFingerGesture.startEvents(x = 0f, y = 0f),
+            expectedState = InProgress(progress = 0f),
+        )
+    }
+
+    @Test
+    fun triggersGestureError_onGestureDistanceTooShort() {
+        assertStateAfterEvents(events = tooShortGesture, expectedState = Error)
+    }
+
+    @Test
+    fun triggersGestureError_onThreeFingersSwipeInOtherDirections() {
+        val allThreeFingerGestures =
+            listOf(
+                ThreeFingerGesture.swipeUp(),
+                ThreeFingerGesture.swipeDown(),
+                ThreeFingerGesture.swipeLeft(),
+                ThreeFingerGesture.swipeRight(),
+            )
+        val invalidGestures = allThreeFingerGestures.filter { it.differentFromAnyOf(validGestures) }
+        invalidGestures.forEach { assertStateAfterEvents(events = it, expectedState = Error) }
+    }
+
+    @Test
+    fun triggersGestureError_onTwoFingersSwipe() {
+        assertStateAfterEvents(events = TwoFingerGesture.swipeRight(), expectedState = Error)
+    }
+
+    @Test
+    fun doesntTriggerGestureError_TwoFingerSwipeInProgress() {
+        assertStateAfterEvents(
+            events = TwoFingerGesture.eventsForGestureInProgress { move(deltaX = SWIPE_DISTANCE) },
+            expectedState = NotStarted,
+        )
+    }
+
+    @Test
+    fun triggersGestureError_onFourFingersSwipe() {
+        assertStateAfterEvents(events = FourFingerGesture.swipeRight(), expectedState = Error)
+    }
+
+    @Test
+    fun doesntTriggerGestureError_FourFingerSwipeInProgress() {
+        assertStateAfterEvents(
+            events = FourFingerGesture.eventsForGestureInProgress { move(deltaX = SWIPE_DISTANCE) },
+            expectedState = NotStarted,
+        )
+    }
+
+    @Test
+    fun ignoresOneFingerSwipes() {
+        val oneFingerSwipe =
+            listOf(
+                touchpadEvent(MotionEvent.ACTION_DOWN, 50f, 50f),
+                touchpadEvent(MotionEvent.ACTION_MOVE, 55f, 55f),
+                touchpadEvent(MotionEvent.ACTION_UP, 60f, 60f),
+            )
+        assertStateAfterEvents(events = oneFingerSwipe, expectedState = NotStarted)
+    }
+
+    private fun assertStateAfterEvents(events: List<MotionEvent>, expectedState: GestureState) {
+        events.forEach { recognizer.accept(it) }
+        assertThat(gestureState).isEqualTo(expectedState)
+    }
+
+    companion object {
+        @JvmStatic
+        @Parameters(name = "{3}")
+        fun gesturesToTest(): List<Array<Any>> =
+            with(ThreeFingerGesture) {
+                listOf(
+                        GestureTestData(
+                            recognizer = BackGestureRecognizer(SWIPE_DISTANCE.toInt()),
+                            validGestures = setOf(swipeRight(), swipeLeft()),
+                            tooShortGesture = swipeRight(SWIPE_DISTANCE / 2),
+                            testSuffix = "back gesture",
+                        ),
+                        GestureTestData(
+                            recognizer =
+                                HomeGestureRecognizer(
+                                    SWIPE_DISTANCE.toInt(),
+                                    THRESHOLD_VELOCITY_PX_PER_MS,
+                                    FakeVelocityTracker(velocity = FAST),
+                                ),
+                            validGestures = setOf(swipeUp()),
+                            tooShortGesture = swipeUp(SWIPE_DISTANCE / 2),
+                            testSuffix = "home gesture",
+                        ),
+                        GestureTestData(
+                            recognizer =
+                                RecentAppsGestureRecognizer(
+                                    SWIPE_DISTANCE.toInt(),
+                                    THRESHOLD_VELOCITY_PX_PER_MS,
+                                    FakeVelocityTracker(velocity = SLOW),
+                                ),
+                            validGestures = setOf(swipeUp()),
+                            tooShortGesture = swipeUp(SWIPE_DISTANCE / 2),
+                            testSuffix = "recent apps gesture",
+                        ),
+                    )
+                    .map {
+                        arrayOf(it.recognizer, it.validGestures, it.tooShortGesture, it.testSuffix)
+                    }
+            }
+    }
+
+    class GestureTestData(
+        val recognizer: GestureRecognizer,
+        val validGestures: Set<List<MotionEvent>>,
+        val tooShortGesture: List<MotionEvent>,
+        val testSuffix: String,
+    )
+}
+
+private fun List<MotionEvent>.differentFromAnyOf(validGestures: Set<List<MotionEvent>>): Boolean {
+    // comparing MotionEvents is really hard so let's just compare their positions
+    val positions = this.map { it.x to it.y }
+    val validGesturesPositions = validGestures.map { gesture -> gesture.map { it.x to it.y } }
+    return !validGesturesPositions.contains(positions)
+}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockFaceLayout.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockFaceLayout.kt
index fb5ef02..843afb8 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockFaceLayout.kt
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockFaceLayout.kt
@@ -115,14 +115,25 @@
                 // and we're not planning to add this vide in clockHostView
                 // so we only need position of device entry icon to constrain clock
                 // Copied calculation codes from applyConstraints in DefaultDeviceEntrySection
-                val bottomPaddingPx = getDimen(context, "lock_icon_margin_bottom")
-                val defaultDensity =
-                    DisplayMetrics.DENSITY_DEVICE_STABLE.toFloat() /
-                        DisplayMetrics.DENSITY_DEFAULT.toFloat()
-                val lockIconRadiusPx = (defaultDensity * 36).toInt()
-                val clockBottomMargin = bottomPaddingPx + 2 * lockIconRadiusPx
+                clockPreviewConfig.lockId?.let { lockId ->
+                    connect(lockscreenClockViewLargeId, BOTTOM, lockId, TOP)
+                }
+                    ?: run {
+                        val bottomPaddingPx = getDimen(context, "lock_icon_margin_bottom")
+                        val defaultDensity =
+                            DisplayMetrics.DENSITY_DEVICE_STABLE.toFloat() /
+                                DisplayMetrics.DENSITY_DEFAULT.toFloat()
+                        val lockIconRadiusPx = (defaultDensity * 36).toInt()
+                        val clockBottomMargin = bottomPaddingPx + 2 * lockIconRadiusPx
+                        connect(
+                            lockscreenClockViewLargeId,
+                            BOTTOM,
+                            PARENT_ID,
+                            BOTTOM,
+                            clockBottomMargin,
+                        )
+                    }
 
-                connect(lockscreenClockViewLargeId, BOTTOM, PARENT_ID, BOTTOM, clockBottomMargin)
                 val smallClockViewId = getId(context, "lockscreen_clock_view")
                 constrainWidth(smallClockViewId, WRAP_CONTENT)
                 constrainHeight(smallClockViewId, getDimen(context, "small_clock_height"))
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockPreviewConfig.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockPreviewConfig.kt
index 544b705..94e669f 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockPreviewConfig.kt
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockPreviewConfig.kt
@@ -22,4 +22,5 @@
     val previewContext: Context,
     val isShadeLayoutWide: Boolean,
     val isSceneContainerFlagEnabled: Boolean = false,
+    val lockId: Int? = null,
 )
diff --git a/packages/SystemUI/res-product/values/strings.xml b/packages/SystemUI/res-product/values/strings.xml
index 42733a2..0c29bb4 100644
--- a/packages/SystemUI/res-product/values/strings.xml
+++ b/packages/SystemUI/res-product/values/strings.xml
@@ -179,6 +179,8 @@
     <!-- Text informing the user that their media is now playing on this tablet device. [CHAR LIMIT=50] -->
     <string name="media_transfer_playing_this_device" product="tablet">Playing on this tablet</string>
 
-
+    <!-- Message shown during shutdown when Find My Device with Dead Battery Finder is active  [CHAR LIMIT=300] -->
+    <string name="finder_active" product="default">You can locate this phone with Find My Device even when powered off</string>
+    <string name="finder_active" product="tablet">You can locate this tablet with Find My Device even when powered off</string>
 
 </resources>
diff --git a/packages/SystemUI/res/layout/ongoing_activity_chip.xml b/packages/SystemUI/res/layout/ongoing_activity_chip.xml
index 215e4e4..7745af9 100644
--- a/packages/SystemUI/res/layout/ongoing_activity_chip.xml
+++ b/packages/SystemUI/res/layout/ongoing_activity_chip.xml
@@ -49,27 +49,25 @@
              ongoing_activity_chip_short_time_delta] will ever be shown at one time. -->
 
         <!-- Shows a timer, like 00:01. -->
+        <!-- Don't use the LimitedWidth style for the timer because the end of the timer is often
+             the most important value. ChipChronometer has the correct logic for when the timer is
+             too large for the space allowed. -->
         <com.android.systemui.statusbar.chips.ui.view.ChipChronometer
             android:id="@+id/ongoing_activity_chip_time"
             style="@style/StatusBar.Chip.Text"
         />
 
         <!-- Shows generic text. -->
-        <!-- Since there's so little room in the status bar chip area, don't ellipsize the text and
-             instead just fade it out a bit at the end. -->
         <TextView
             android:id="@+id/ongoing_activity_chip_text"
-            style="@style/StatusBar.Chip.Text"
-            android:ellipsize="none"
-            android:requiresFadingEdge="horizontal"
-            android:fadingEdgeLength="@dimen/ongoing_activity_chip_text_fading_edge_length"
+            style="@style/StatusBar.Chip.Text.LimitedWidth"
             android:visibility="gone"
             />
 
         <!-- Shows a time delta in short form, like "15min" or "1hr". -->
         <android.widget.DateTimeView
             android:id="@+id/ongoing_activity_chip_short_time_delta"
-            style="@style/StatusBar.Chip.Text"
+            style="@style/StatusBar.Chip.Text.LimitedWidth"
             android:visibility="gone"
             />
 
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index cef0316..1d0524a 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -529,9 +529,9 @@
     <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>
     <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Legstukke"</string>
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
-    <skip />
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
+    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Maak seker dat “Wys legstukke op sluitskerm” in instellings geaktiveer is om die “Legstukke”-kortpad by te voeg."</string>
+    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Instellings"</string>
+    <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
     <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Wissel gebruiker"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"aftrekkieslys"</string>
@@ -593,8 +593,7 @@
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Kennisgewings"</string>
     <string name="notification_section_header_conversations" msgid="821834744538345661">"Gesprekke"</string>
     <string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Vee alle stil kennisgewings uit"</string>
-    <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
-    <skip />
+    <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Maak kennisgewinginstellings oop"</string>
     <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Kennisgewings onderbreek deur Moenie Steur Nie"</string>
     <string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Geen kennisgewings nie}=1{Kennisgewings is deur {mode} onderbreek}=2{Kennisgewings is deur {mode} en een ander modus onderbreek}other{Kennisgewings is deur {mode} en # ander modusse onderbreek}}"</string>
     <string name="media_projection_action_text" msgid="3634906766918186440">"Begin nou"</string>
@@ -664,21 +663,21 @@
     <string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Oorfoonvolume het die veilige limiet vir hierdie week oorskry"</string>
     <string name="csd_button_keep_listening" product="default" msgid="4093794049149286784">"Hou aan luister"</string>
     <string name="csd_button_lower_volume" product="default" msgid="5347210412376264579">"Stel volume sagter"</string>
-    <string name="screen_pinning_title" msgid="9058007390337841305">"Program is vasgespeld"</string>
+    <string name="screen_pinning_title" msgid="9058007390337841305">"App is vasgespeld"</string>
     <string name="screen_pinning_description" msgid="8699395373875667743">"Dit hou dit in sig totdat jy dit ontspeld. Raak en hou Terug en Oorsig om dit te ontspeld."</string>
     <string name="screen_pinning_description_recents_invisible" msgid="4564466648700390037">"Dit hou dit in sig totdat jy dit ontspeld. Raak en hou Terug en Tuis om dit te ontspeld."</string>
     <string name="screen_pinning_description_gestural" msgid="7246323931831232068">"Dit hou dit in sig totdat jy dit ontspeld. Swiep op en hou om te ontspeld."</string>
     <string name="screen_pinning_description_accessible" msgid="7386449191953535332">"Dit hou dit in sig totdat jy dit ontspeld. Raak en hou Oorsig om dit te ontspeld."</string>
     <string name="screen_pinning_description_recents_invisible_accessible" msgid="2857071808674481986">"Dit hou dit in sig totdat jy dit ontspeld. Raak en hou Tuis om dit te ontspeld."</string>
     <string name="screen_pinning_exposes_personal_data" msgid="8189852022981524789">"Persoonlike data (soos kontakte en e-posinhoud) kan toeganklik wees."</string>
-    <string name="screen_pinning_can_open_other_apps" msgid="7529756813231421455">"Vasgespelde program kan ander programme oopmaak."</string>
-    <string name="screen_pinning_toast" msgid="8177286912533744328">"Raak en hou die terug- en oorsigknoppie om hierdie program te ontspeld"</string>
-    <string name="screen_pinning_toast_recents_invisible" msgid="6850978077443052594">"Raak en hou die terug- en tuisknoppie om hierdie program te ontspeld"</string>
-    <string name="screen_pinning_toast_gesture_nav" msgid="170699893395336705">"Swiep op en hou om hierdie program te ontspeld"</string>
+    <string name="screen_pinning_can_open_other_apps" msgid="7529756813231421455">"Vasgespelde app kan ander apps oopmaak."</string>
+    <string name="screen_pinning_toast" msgid="8177286912533744328">"Raak en hou die terug- en oorsigknoppie om hierdie app te ontspeld"</string>
+    <string name="screen_pinning_toast_recents_invisible" msgid="6850978077443052594">"Raak en hou die terug- en tuisknoppie om hierdie app te ontspeld"</string>
+    <string name="screen_pinning_toast_gesture_nav" msgid="170699893395336705">"Swiep op en hou om hierdie app te ontspeld"</string>
     <string name="screen_pinning_positive" msgid="3285785989665266984">"Het dit"</string>
     <string name="screen_pinning_negative" msgid="6882816864569211666">"Nee, dankie"</string>
-    <string name="screen_pinning_start" msgid="7483998671383371313">"Program is vasgespeld"</string>
-    <string name="screen_pinning_exit" msgid="4553787518387346893">"Program is ontspeld"</string>
+    <string name="screen_pinning_start" msgid="7483998671383371313">"App is vasgespeld"</string>
+    <string name="screen_pinning_exit" msgid="4553787518387346893">"App is ontspeld"</string>
     <string name="stream_voice_call" msgid="7468348170702375660">"Bel"</string>
     <string name="stream_system" msgid="7663148785370565134">"Stelsel"</string>
     <string name="stream_ring" msgid="7550670036738697526">"Lui"</string>
@@ -709,8 +708,7 @@
     <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Kopnasporing"</string>
     <string name="volume_ringer_change" msgid="3574969197796055532">"Tik om luiermodus te verander"</string>
     <string name="volume_ringer_mode" msgid="6867838048430807128">"luiermodus"</string>
-    <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
-    <skip />
+    <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>; tik om luiermodus te verander"</string>
     <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"demp"</string>
     <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ontdemp"</string>
     <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibreer"</string>
@@ -875,9 +873,12 @@
     <string name="group_system_lock_screen" msgid="7391191300363416543">"Sluit skerm"</string>
     <string name="group_system_quick_memo" msgid="3764560265935722903">"Maak ’n nota"</string>
     <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Verrigting van veelvuldige take"</string>
-    <string name="system_multitasking_rhs" msgid="8714224917276297810">"Gebruik verdeelde skerm met huidige app aan die regterkant"</string>
-    <string name="system_multitasking_lhs" msgid="8402954791206308783">"Gebruik verdeelde skerm met huidige app aan die linkerkant"</string>
-    <string name="system_multitasking_full_screen" msgid="336048080383640562">"Skakel oor van verdeelde skerm na volskerm"</string>
+    <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+    <skip />
+    <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+    <skip />
+    <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+    <skip />
     <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Skakel oor na app regs of onder terwyl jy verdeelde skerm gebruik"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Skakel oor na app links of bo terwyl jy verdeelde skerm gebruik"</string>
     <string name="system_multitasking_replace" msgid="7410071959803642125">"Tydens verdeelde skerm: verplaas ’n app van een skerm na ’n ander"</string>
@@ -1117,7 +1118,7 @@
     <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"wissel"</string>
     <string name="accessibility_floating_button_action_edit" msgid="1688227814600463987">"Wysig"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Toestelkontroles"</string>
-    <string name="controls_providers_title" msgid="6879775889857085056">"Kies program om kontroles by te voeg"</string>
+    <string name="controls_providers_title" msgid="6879775889857085056">"Kies app om kontroles by te voeg"</string>
     <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# kontrole bygevoeg.}other{# kontroles bygevoeg.}}"</string>
     <string name="controls_removed" msgid="3731789252222856959">"Verwyder"</string>
     <string name="controls_panel_authorization_title" msgid="267429338785864842">"Voeg <xliff:g id="APPNAME">%s</xliff:g> by?"</string>
@@ -1138,7 +1139,7 @@
     <string name="controls_favorite_rearrange_button" msgid="2942788904364641185">"Herrangskik"</string>
     <string name="controls_favorite_add_controls" msgid="1221420435546694004">"Voeg kontroles by"</string>
     <string name="controls_favorite_back_to_editing" msgid="184125114090062713">"Terug na wysiging"</string>
-    <string name="controls_favorite_load_error" msgid="5126216176144877419">"Kontroles kon nie gelaai word nie. Gaan die <xliff:g id="APP">%s</xliff:g>-program na om seker te maak dat die programinstellings nie verander het nie."</string>
+    <string name="controls_favorite_load_error" msgid="5126216176144877419">"Kontroles kon nie gelaai word nie. Gaan die <xliff:g id="APP">%s</xliff:g>-app na om seker te maak dat die appinstellings nie verander het nie."</string>
     <string name="controls_favorite_load_none" msgid="7687593026725357775">"Versoenbare kontroles is nie beskikbaar nie"</string>
     <string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"Ander"</string>
     <string name="controls_dialog_title" msgid="2343565267424406202">"Voeg by toestelkontroles"</string>
@@ -1189,10 +1190,10 @@
     <string name="media_ttt_default_device_type" msgid="4457646436153370169">"tablet"</string>
     <string name="media_transfer_receiver_content_description_unknown_app" msgid="7381771464846263667">"Saai jou media uit"</string>
     <string name="media_transfer_receiver_content_description_with_app_name" msgid="8555975056850659389">"Saai tans <xliff:g id="APP_LABEL">%1$s</xliff:g> uit"</string>
-    <string name="controls_error_timeout" msgid="794197289772728958">"Onaktief, gaan program na"</string>
+    <string name="controls_error_timeout" msgid="794197289772728958">"Onaktief, gaan app na"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Nie gekry nie"</string>
     <string name="controls_error_removed_title" msgid="1207794911208047818">"Kontrole is nie beskikbaar nie"</string>
-    <string name="controls_error_removed_message" msgid="2885911717034750542">"Kon nie by <xliff:g id="DEVICE">%1$s</xliff:g> ingaan nie. Gaan die <xliff:g id="APPLICATION">%2$s</xliff:g>-program na om seker te maak dat die kontrole steeds beskikbaar is en dat die programinstellings nie verander het nie."</string>
+    <string name="controls_error_removed_message" msgid="2885911717034750542">"Kon nie by <xliff:g id="DEVICE">%1$s</xliff:g> ingaan nie. Gaan die <xliff:g id="APPLICATION">%2$s</xliff:g>-app na om seker te maak dat die kontrole steeds beskikbaar is en dat die appinstellings nie verander het nie."</string>
     <string name="controls_open_app" msgid="483650971094300141">"Maak app oop"</string>
     <string name="controls_error_generic" msgid="352500456918362905">"Kan nie status laai nie"</string>
     <string name="controls_error_failed" msgid="960228639198558525">"Fout, probeer weer"</string>
@@ -1430,20 +1431,17 @@
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Kortpadsleutels"</string>
     <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Pasmaak kortpadsleutels"</string>
     <string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Verwyder kortpad?"</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Stel terug na verstek?"</string>
     <string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Druk sleutel om kortpad toe te wys"</string>
     <string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Dit sal jou gepasmaakte kortpad permanent uitvee."</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Dit sal al jou gepasmaakte kortpaaie permanent uitvee."</string>
     <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>
     <string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Ikoon vir Handeling- of Meta-sleutel"</string>
     <string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Plusikoon"</string>
     <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Pasmaak"</string>
-    <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
-    <skip />
+    <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Stel terug"</string>
     <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Klaar"</string>
     <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>
@@ -1453,8 +1451,7 @@
     <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Sleutelbordinstellings"</string>
     <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Stel kortpad"</string>
     <string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Verwyder"</string>
-    <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
-    <skip />
+    <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Ja, stel terug"</string>
     <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Kanselleer"</string>
     <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Druk sleutel"</string>
     <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Sleutelkombinasie is reeds in gebruik. Probeer ’n ander sleutel."</string>
@@ -1486,8 +1483,7 @@
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Druk die handelingsleutel op jou sleutelbord"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Welgedaan!"</string>
     <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Jy het die Bekyk Onlangse Apps-gebaar voltooi"</string>
-    <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
-    <skip />
+    <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Tutoriaalanimasie; klik om te onderbreek of hervat om te speel."</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Sleutelbordlig"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Vlak %1$d van %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Huiskontroles"</string>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index 5f77743..687bd08 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -531,6 +531,8 @@
     <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"ምግብሮች"</string>
     <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"የ«ምግብሮች» አቋራጭን ለማከል በቅንብሮች ውስጥ «ምግብሮችን በማያ ገፅ ቁልፍ ላይ አሳይ» የሚለው መንቃቱን ያረጋግጡ።"</string>
     <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"ቅንብሮች"</string>
+    <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
+    <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ተጠቃሚ ቀይር"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ወደታች ተጎታች ምናሌ"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"በዚህ ክፍለ-ጊዜ ውስጥ ያሉ ሁሉም መተግበሪያዎች እና ውሂብ ይሰረዛሉ።"</string>
@@ -591,8 +593,7 @@
     <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>
-    <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
-    <skip />
+    <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"የማሳወቂያ ቅንብሮችን ክፈት"</string>
     <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"ማሳወቂያዎች በአትረብሽ ባሉበት ቆመዋል"</string>
     <string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{ምንም ማሳወቂያዎች የሉም}=1{ማሳወቂያዎች በ{mode} ባሉበት ቆመዋል}=2{ማሳወቂያዎች በ{mode} እና አንድ ሌላ ሁነታ ባሉበት ቆመዋል}one{ማሳወቂያዎች በ{mode} እና # ሌላ ሁነታ ባሉበት ቆመዋል}other{ማሳወቂያዎች በ{mode} እና # ሌላ ሁነታዎች ባሉበት ቆመዋል}}"</string>
     <string name="media_projection_action_text" msgid="3634906766918186440">"አሁን ጀምር"</string>
@@ -707,8 +708,7 @@
     <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"የጭንቅላት ክትትል"</string>
     <string name="volume_ringer_change" msgid="3574969197796055532">"የደዋይ ሁነታን ለመቀየር መታ ያድርጉ"</string>
     <string name="volume_ringer_mode" msgid="6867838048430807128">"ደዋይ ሁነታ"</string>
-    <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
-    <skip />
+    <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>፣ የደዋይ ሁነታን ለመቀየር መታ ያድርጉ"</string>
     <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"ድምጸ-ከል አድርግ"</string>
     <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ድምጸ-ከልን አንሳ"</string>
     <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"ንዘር"</string>
@@ -873,9 +873,12 @@
     <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_full_screen" msgid="336048080383640562">"ከየተከፈለ ማያ ገጽ ወደ ሙሉ ገጽ ዕይታ ቀይር"</string>
+    <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+    <skip />
+    <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+    <skip />
+    <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+    <skip />
     <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>
@@ -1438,8 +1441,7 @@
     <string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"የእርምጃ ወይም ሜታ ቁልፍ አዶ"</string>
     <string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"የመደመር አዶ"</string>
     <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"አብጅ"</string>
-    <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
-    <skip />
+    <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"ዳግም አስጀምር"</string>
     <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"ተከናውኗል"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"መዘርጊያ አዶ"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ወይም"</string>
@@ -1481,8 +1483,7 @@
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"በቁልፍ ሰሌዳዎ ላይ ያለውን የተግባር ቁልፍ ይጫኑ"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"ጥሩ ሠርተዋል!"</string>
     <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"የሁሉንም መተግበሪያዎች አሳይ ምልክትን አጠናቅቀዋል"</string>
-    <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
-    <skip />
+    <string name="tutorial_animation_content_description" msgid="2698816574982370184">"የአጋዥ ሥልጠና እነማ፣ ማጫወትን ባለበት ለማቆም እና ከቆመበት ለመቀጠል ጠቅ ያድርጉ።"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"የቁልፍ ሰሌዳ የጀርባ ብርሃን"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"ደረጃ %1$d ከ %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"የቤት ውስጥ ቁጥጥሮች"</string>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index dd6e850..0fc6598 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -531,6 +531,7 @@
     <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"التطبيقات المصغَّرة"</string>
     <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"لإضافة اختصار \"التطبيقات المصغّرة\"، يجب تفعيل خيار \"عرض التطبيقات المصغّرة على شاشة القفل\" من خلال الإعدادات."</string>
     <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"الإعدادات"</string>
+    <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"زر \"إظهار شاشة الاستراحة\""</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"تبديل المستخدم"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"القائمة المنسدلة"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"سيتم حذف كل التطبيقات والبيانات في هذه الجلسة."</string>
@@ -707,8 +708,7 @@
     <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"تتبُّع حركة الرأس"</string>
     <string name="volume_ringer_change" msgid="3574969197796055532">"انقر لتغيير وضع الرنين."</string>
     <string name="volume_ringer_mode" msgid="6867838048430807128">"وضع الرنين"</string>
-    <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
-    <skip />
+    <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"الحالة: <xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>. انقر لتغيير وضع الرنين"</string>
     <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"كتم الصوت"</string>
     <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"إعادة الصوت"</string>
     <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"اهتزاز"</string>
@@ -873,9 +873,12 @@
     <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_full_screen" msgid="336048080383640562">"التبديل من وضع \"تقسيم الشاشة\" إلى وضع \"ملء الشاشة\""</string>
+    <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+    <skip />
+    <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+    <skip />
+    <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+    <skip />
     <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>
@@ -1438,8 +1441,7 @@
     <string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"‏رمز مفتاح الإجراء (مفتاح Meta)"</string>
     <string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"رمز علامة الجمع (+)"</string>
     <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"تخصيص"</string>
-    <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
-    <skip />
+    <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"إعادة الضبط"</string>
     <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"تم"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"رمز التوسيع"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"أو"</string>
@@ -1481,8 +1483,7 @@
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"اضغط على مفتاح الإجراء في لوحة المفاتيح"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"أحسنت!"</string>
     <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"لقد أكملْت التدريب على إيماءة عرض جميع التطبيقات"</string>
-    <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
-    <skip />
+    <string name="tutorial_animation_content_description" msgid="2698816574982370184">"صورة متحركة للدليل التوجيهي: يمكنك النقر عليها لإيقاف تشغيلها مؤقتًا واستئنافه."</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"الإضاءة الخلفية للوحة المفاتيح"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"‏مستوى الإضاءة: %1$d من %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"إدارة المنزل آليًّا"</string>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index 86e13055..b0a9d09 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -531,6 +531,7 @@
     <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"ৱিজেট"</string>
     <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"\"ৱিজেট\"ৰ শ্বৰ্টকাট যোগ দিবলৈ, ছেটিঙত \"লক স্ক্ৰীনত ৱিজেট দেখুৱাওক\" সক্ষম কৰি থোৱাটো নিশ্চিত কৰক।"</string>
     <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"ছেটিং"</string>
+    <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"স্ক্ৰীনছেভাৰৰ বুটাম দেখুৱাওক"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ব্যৱহাৰকাৰী সলনি কৰক"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"পুল-ডাউনৰ মেনু"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"এই ছেশ্বনৰ আটাইবোৰ এপ্ আৰু ডেটা মচা হ\'ব।"</string>
@@ -591,8 +592,7 @@
     <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>
-    <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
-    <skip />
+    <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"জাননীৰ ছেটিং খোলক"</string>
     <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"অসুবিধা নিদিব-ই জাননী পজ কৰিছে"</string>
     <string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{কোনো জাননী নাই}=1{{mode}এ জাননী পজ কৰিছে}=2{{mode} আৰু আন এটা ম’ডে জাননী পজ কৰিছে}one{{mode} আৰু আন # টা ম’ডে জাননী পজ কৰিছে}other{{mode} আৰু আন # টা ম’ডে জাননী পজ কৰিছে}}"</string>
     <string name="media_projection_action_text" msgid="3634906766918186440">"এতিয়াই আৰম্ভ কৰক"</string>
@@ -707,8 +707,7 @@
     <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"হে’ড ট্ৰেকিং"</string>
     <string name="volume_ringer_change" msgid="3574969197796055532">"ৰিংগাৰ ম’ড সলনি কৰিবলৈ টিপক"</string>
     <string name="volume_ringer_mode" msgid="6867838048430807128">"ৰিংগাৰ ম’ড"</string>
-    <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
-    <skip />
+    <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, ৰিংগাৰ ম’ড সলনি কৰিবলৈ টিপক"</string>
     <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"মিউট কৰক"</string>
     <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"আনমিউট কৰক"</string>
     <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"কম্পন কৰক"</string>
@@ -873,9 +872,12 @@
     <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_full_screen" msgid="336048080383640562">"বিভাজিত স্ক্ৰীনৰ পৰা পূৰ্ণ স্ক্ৰীনলৈ সলনি কৰক"</string>
+    <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+    <skip />
+    <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+    <skip />
+    <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+    <skip />
     <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>
@@ -1438,8 +1440,7 @@
     <string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"কাৰ্য বা মেটা কীৰ চিহ্ন"</string>
     <string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"যোগ চিনৰ চিহ্ন"</string>
     <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"কাষ্টমাইজ কৰক"</string>
-    <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
-    <skip />
+    <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"ৰিছেট কৰক"</string>
     <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"হ’ল"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"বিস্তাৰ কৰাৰ চিহ্ন"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"অথবা"</string>
@@ -1481,8 +1482,7 @@
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"আপোনাৰ কীব’ৰ্ডৰ কাৰ্য কীটোত টিপক"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"বঢ়িয়া!"</string>
     <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"আপুনি আটাইবোৰ এপ্ চোৱাৰ নিৰ্দেশনাটো সম্পূৰ্ণ কৰিছে"</string>
-    <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
-    <skip />
+    <string name="tutorial_animation_content_description" msgid="2698816574982370184">"টিউট’ৰিয়েল এনিমেশ্বন, পজ কৰিবলৈ আৰু প্লে’ কৰাটো পুনৰ আৰম্ভ কৰিবলৈ ক্লিক কৰক।"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"কীব’ৰ্ডৰ বেকলাইট"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$dৰ %1$d স্তৰ"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"ঘৰৰ সা-সৰঞ্জামৰ নিয়ন্ত্ৰণ"</string>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index 3082284..0f59bfb 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -529,9 +529,9 @@
     <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>
     <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Vidcetlər"</string>
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
-    <skip />
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
+    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"\"Vidcetlər\" qısayolunu əlavə etmək üçün ayarlarda \"Vidcetləri kilidli ekranda göstərin\" seçimi aktiv olmalıdır."</string>
+    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Ayarlar"</string>
+    <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
     <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Switch user"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"aşağı çəkilən menyu"</string>
@@ -593,8 +593,7 @@
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Bildirişlər"</string>
     <string name="notification_section_header_conversations" msgid="821834744538345661">"Söhbətlər"</string>
     <string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Səssiz bildirişlərin hamısını silin"</string>
-    <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
-    <skip />
+    <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Bildiriş ayarlarını açın"</string>
     <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Bildirişlər \"Narahat Etməyin\" rejimi tərəfindən dayandırıldı"</string>
     <string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Bildiriş yoxdur}=1{Bildirişlər {mode} tərəfindən dayandırıldı}=2{Bildirişlər {mode} və digər rejim tərəfindən dayandırıldı}other{Bildirişlər {mode} və # digər rejim tərəfindən dayandırıldı}}"</string>
     <string name="media_projection_action_text" msgid="3634906766918186440">"İndi başlayın"</string>
@@ -709,8 +708,7 @@
     <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Baş izləməsi"</string>
     <string name="volume_ringer_change" msgid="3574969197796055532">"Zəng rejimini dəyişmək üçün toxunun"</string>
     <string name="volume_ringer_mode" msgid="6867838048430807128">"zəng səsi rejimi"</string>
-    <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
-    <skip />
+    <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, zəng səsi rejimini dəyişmək üçün toxunun"</string>
     <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"susdurun"</string>
     <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"səssiz rejimdən çıxarın"</string>
     <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrasiya"</string>
@@ -875,9 +873,12 @@
     <string name="group_system_lock_screen" msgid="7391191300363416543">"Kilid ekranı"</string>
     <string name="group_system_quick_memo" msgid="3764560265935722903">"Qeyd götürün"</string>
     <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Çoxsaylı tapşırıq icrası"</string>
-    <string name="system_multitasking_rhs" msgid="8714224917276297810">"Cari tətbiq sağda olmaqla bölünmüş ekrandan istifadə edin"</string>
-    <string name="system_multitasking_lhs" msgid="8402954791206308783">"Cari tətbiq solda olmaqla bölünmüş ekrandan istifadə edin"</string>
-    <string name="system_multitasking_full_screen" msgid="336048080383640562">"Bölünmüş ekrandan tam ekrana keçin"</string>
+    <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+    <skip />
+    <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+    <skip />
+    <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+    <skip />
     <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Bölünmüş ekran istifadə edərkən sağda və ya aşağıda tətbiqə keçin"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Bölünmüş ekran istifadə edərkən solda və ya yuxarıda tətbiqə keçin"</string>
     <string name="system_multitasking_replace" msgid="7410071959803642125">"Bölünmüş ekran rejimində: tətbiqi birindən digərinə dəyişin"</string>
@@ -1430,20 +1431,17 @@
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Klaviatura qısayolları"</string>
     <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Klaviatura qısayollarını fərdiləşdirin"</string>
     <string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Qısayol silinsin?"</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Defolt vəziyyətə qaytarılsın?"</string>
     <string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Qısayol təyin etmək üçün düyməni basın"</string>
     <string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Bu, fərdi qısayolunuzu həmişəlik siləcək."</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Bu, bütün fərdi qısayollarınızı həmişəlik siləcək."</string>
     <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>
     <string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Əməliyyat və ya Meta düyməsi ikonası"</string>
     <string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Üstəgəl ikonası"</string>
     <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Fərdiləşdirin"</string>
-    <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
-    <skip />
+    <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Sıfırlayın"</string>
     <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Hazırdır"</string>
     <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>
@@ -1453,8 +1451,7 @@
     <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Klaviatura ayarları"</string>
     <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Qısayol ayarlayın"</string>
     <string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Silin"</string>
-    <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
-    <skip />
+    <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Bəli, sıfırlayın"</string>
     <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Ləğv edin"</string>
     <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Düyməni basın"</string>
     <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Düymə kombinasiyası artıq istifadə olunur. Başqa düyməni sınayın."</string>
@@ -1486,8 +1483,7 @@
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Klaviaturada fəaliyyət açarına basın"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Əla!"</string>
     <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"\"Bütün tətbiqlərə baxın\" jestini tamamladınız"</string>
-    <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
-    <skip />
+    <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Öyrədici animasiya, oxudulmanı durdurmaq və davam etdirmək üçün klikləyin."</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Klaviatura işığı"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Səviyyə %1$d/%2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Ev nizamlayıcıları"</string>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index 09aef06..a339b11 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -531,6 +531,7 @@
     <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Vidžeti"</string>
     <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Da biste dodali prečicu Vidžeti, uverite se da je u podešavanjima omogućeno Prikazuj vidžete na zaključanom ekranu."</string>
     <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Podešavanja"</string>
+    <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Dugme Prikaži čuvar ekrana"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Zameni korisnika"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"padajući meni"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Sve aplikacije i podaci u ovoj sesiji će biti izbrisani."</string>
@@ -591,8 +592,7 @@
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Obaveštenja"</string>
     <string name="notification_section_header_conversations" msgid="821834744538345661">"Konverzacije"</string>
     <string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Obrišite sva nečujna obaveštenja"</string>
-    <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
-    <skip />
+    <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Otvorite podešavanja obaveštenja"</string>
     <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Obaveštenja su pauzirana režimom Ne uznemiravaj"</string>
     <string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Nema obaveštenja}=1{Obaveštenja je pauzirao {mode}}=2{Obaveštenja su pauzirali {mode} i još jedan režim}one{Obaveštenja su pauzirali {mode} i još # režim}few{Obaveštenja su pauzirali {mode} i još # režima}other{Obaveštenja su pauzirali {mode} i još # režima}}"</string>
     <string name="media_projection_action_text" msgid="3634906766918186440">"Započni"</string>
@@ -707,8 +707,7 @@
     <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Praćenje glave"</string>
     <string name="volume_ringer_change" msgid="3574969197796055532">"Dodirnite da biste promenili režim zvona"</string>
     <string name="volume_ringer_mode" msgid="6867838048430807128">"režim zvona"</string>
-    <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
-    <skip />
+    <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, dodirnite da biste promenili režim zvona"</string>
     <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"isključite zvuk"</string>
     <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"uključite zvuk"</string>
     <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibracija"</string>
@@ -873,9 +872,12 @@
     <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">"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>
+    <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+    <skip />
+    <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+    <skip />
+    <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+    <skip />
     <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>
@@ -1438,8 +1440,7 @@
     <string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Ikona tastera za radnju ili meta tastera"</string>
     <string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Ikona znaka plus"</string>
     <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Prilagodi"</string>
-    <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
-    <skip />
+    <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Resetuj"</string>
     <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Gotovo"</string>
     <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>
@@ -1481,8 +1482,7 @@
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Pritisnite taster radnji na tastaturi"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Odlično!"</string>
     <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Dovršili ste pokret za prikazivanje svih aplikacija."</string>
-    <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
-    <skip />
+    <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Animacija vodiča, kliknite da biste pauzirali i nastavili reprodukciju."</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Pozadinsko osvetljenje tastature"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"%1$d. nivo od %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Kontrole za dom"</string>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index 51cc35a..9c5a4f2 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -531,6 +531,8 @@
     <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Віджэты"</string>
     <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Каб дадаць спалучэнне клавіш \"Віджэты\", у наладах павінна быць уключана функцыя \"Паказваць віджэты на экране блакіроўкі\"."</string>
     <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Налады"</string>
+    <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
+    <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Перайсці да іншага карыстальніка"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"высоўнае меню"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Усе праграмы і даныя гэтага сеанса будуць выдалены."</string>
@@ -591,8 +593,7 @@
     <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>
-    <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
-    <skip />
+    <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Адкрыць налады апавяшчэнняў"</string>
     <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Паказ апавяшчэнняў прыпынены ў рэжыме \"Не турбаваць\""</string>
     <string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Апавяшчэнняў няма}=1{Атрыманне апавяшчэнняў прыпынена рэжымам \"{mode}\"}=2{Атрыманне апавяшчэнняў прыпынена рэжымам \"{mode}\" і яшчэ адным рэжымам}one{Атрыманне апавяшчэнняў прыпынена рэжымам \"{mode}\" і яшчэ # рэжымам}few{Атрыманне апавяшчэнняў прыпынена рэжымам \"{mode}\" і яшчэ # рэжымамі}many{Атрыманне апавяшчэнняў прыпынена рэжымам \"{mode}\" і яшчэ # рэжымамі}other{Атрыманне апавяшчэнняў прыпынена рэжымам \"{mode}\" і яшчэ # рэжыму}}"</string>
     <string name="media_projection_action_text" msgid="3634906766918186440">"Пачаць зараз"</string>
@@ -707,8 +708,7 @@
     <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Адсочваць рух галавы"</string>
     <string name="volume_ringer_change" msgid="3574969197796055532">"Націсніце, каб змяніць рэжым званка"</string>
     <string name="volume_ringer_mode" msgid="6867838048430807128">"рэжым званка"</string>
-    <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
-    <skip />
+    <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>: націсніце, каб змяніць рэжым званка"</string>
     <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"выключыць гук"</string>
     <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"уключыць гук"</string>
     <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"вібрыраваць"</string>
@@ -873,9 +873,12 @@
     <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_full_screen" msgid="336048080383640562">"Пераключыцца з рэжыму падзеленага экрана на поўнаэкранны рэжым"</string>
+    <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+    <skip />
+    <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+    <skip />
+    <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+    <skip />
     <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>
@@ -1438,8 +1441,7 @@
     <string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Значок клавішы дзеяння (мета-клавішы)"</string>
     <string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Значок плюса"</string>
     <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Наладзіць"</string>
-    <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
-    <skip />
+    <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Скінуць"</string>
     <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Гатова"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Значок \"Разгарнуць\""</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"або"</string>
@@ -1481,8 +1483,7 @@
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Націсніце клавішу дзеяння на клавіятуры"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Выдатна!"</string>
     <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Вы навучыліся рабіць жэст для прагляду ўсіх праграм"</string>
-    <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
-    <skip />
+    <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Анімацыя ў дапаможніку: націсніце, каб прыпыніць ці ўзнавіць прайграванне."</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Падсветка клавіятуры"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Узровень %1$d з %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Кіраванне домам"</string>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index 12e25cc..ac2edfe 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -529,10 +529,9 @@
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"За да отворите дадено приложение посредством приспособление, ще трябва да потвърдите, че това сте вие. Също така имайте предвид, че всеки ще вижда приспособленията дори когато таблетът ви е заключен. Възможно е някои от тях да не са предназначени за заключения екран и добавянето им на него може да е опасно."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Разбрах"</string>
     <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Приспособления"</string>
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
-    <skip />
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
-    <skip />
+    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"За да добавите пряк път към „Приспособления“, уверете се, че опцията „Показване на приспособленията на заключения екран“ е активирана в настройките."</string>
+    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Настройки"</string>
+    <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Бутон за показване на скрийнсейвъра"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Превключване между потребителите"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"падащо меню"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Всички приложения и данни в тази сесия ще бъдат изтрити."</string>
@@ -593,8 +592,7 @@
     <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>
-    <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
-    <skip />
+    <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Отваряне на настройките за известията"</string>
     <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Известията са поставени на пауза от режима „Не безпокойте“"</string>
     <string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Няма известия}=1{Известията са поставени на пауза от {mode}}=2{Известията са поставени на пауза от {mode} и един друг режим}other{Известията са поставени на пауза от {mode} и # други режима}}"</string>
     <string name="media_projection_action_text" msgid="3634906766918186440">"Стартиране сега"</string>
@@ -874,9 +872,12 @@
     <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_full_screen" msgid="336048080383640562">"Превключване от разделен към цял екран"</string>
+    <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+    <skip />
+    <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+    <skip />
+    <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+    <skip />
     <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>
@@ -1429,20 +1430,17 @@
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Клавишни комбинации"</string>
     <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Персонализиране на клавишните комбинации"</string>
     <string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Да се премахне ли клавишната комбинация?"</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Да се възстановят ли стандартните настройки?"</string>
     <string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Натиснете клавиш, за да зададете клавишна комбинация"</string>
     <string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Това ще изтрие персонализираната клавишна комбинация за постоянно."</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Това ще изтрие всичките ви персонализирани преки пътища за постоянно."</string>
     <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>
     <string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Икона на клавиша за действия или клавиша Meta"</string>
     <string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Икона на плюс"</string>
     <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Персонализиране"</string>
-    <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
-    <skip />
+    <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Нулиране"</string>
     <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Готово"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Икона за разгъване"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"или"</string>
@@ -1452,8 +1450,7 @@
     <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Настройки на клавиатурата"</string>
     <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Задаване на клавишна комбинация"</string>
     <string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Премахване"</string>
-    <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
-    <skip />
+    <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Да, нулиране"</string>
     <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Отказ"</string>
     <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Натиснете клавиш"</string>
     <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Клавишната комбинация вече се използва. Опитайте с друг клавиш."</string>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index 8af32a8..c35c62a 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -529,9 +529,9 @@
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"উইজেট ব্যবহার করে কোনও অ্যাপ খুলতে, আপনাকে নিজের পরিচয় যাচাই করতে হবে। এছাড়াও, মনে রাখবেন, আপনার ট্যাবলেট লক থাকলেও যেকেউ তা দেখতে পারবেন। কিছু উইজেট আপনার লক স্ক্রিনের উদ্দেশ্যে তৈরি করা হয়নি এবং এখানে যোগ করা নিরাপদ নাও হতে পারে।"</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"বুঝেছি"</string>
     <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"উইজেট"</string>
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
-    <skip />
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
+    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"\"উইজেট\" শর্টকার্ট যোগ করতে, সেটিংস থেকে \"লক স্ক্রিনে উইজেট দেখুন\" বিকল্প চালু আছে কিনা তা নিশ্চিত করুন।"</string>
+    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"সেটিংস"</string>
+    <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
     <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ব্যবহারকারী পাল্টে দিন"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"পুলডাউন মেনু"</string>
@@ -593,8 +593,7 @@
     <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>
-    <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
-    <skip />
+    <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"বিজ্ঞপ্তির সেটিংস খুলুন"</string>
     <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"\'বিরক্ত করবে না\' দিয়ে বিজ্ঞপ্তি পজ করা হয়েছে"</string>
     <string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{কোনও বিজ্ঞপ্তি নেই}=1{{mode}-এর জন্য বিজ্ঞপ্তি পজ করা হয়েছে}=2{{mode} ও অন্য আরেকটি মোডের জন্য বিজ্ঞপ্তি পজ করা হয়েছে}one{{mode} ও অন্য #টি মোডের জন্য বিজ্ঞপ্তি পজ করা হয়েছে}other{{mode} ও অন্য #টি মোডের জন্য বিজ্ঞপ্তি পজ করা হয়েছে}}"</string>
     <string name="media_projection_action_text" msgid="3634906766918186440">"এখন শুরু করুন"</string>
@@ -709,8 +708,7 @@
     <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"হেড ট্র্যাকিং"</string>
     <string name="volume_ringer_change" msgid="3574969197796055532">"রিঙ্গার মোড পরিবর্তন করতে ট্যাপ করুন"</string>
     <string name="volume_ringer_mode" msgid="6867838048430807128">"রিঙ্গার মোড"</string>
-    <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
-    <skip />
+    <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, রিঙ্গার মোড পরিবর্তন করতে ট্যাপ করুন"</string>
     <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"মিউট করুন"</string>
     <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"আনমিউট করুন"</string>
     <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"ভাইব্রেট করান"</string>
@@ -875,9 +873,12 @@
     <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_full_screen" msgid="336048080383640562">"\'স্প্লিট স্ক্রিন\' থেকে ফুল স্ক্রিনে পাল্টান"</string>
+    <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+    <skip />
+    <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+    <skip />
+    <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+    <skip />
     <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>
@@ -1430,20 +1431,17 @@
     <string name="shortcut_helper_title" msgid="8567500639300970049">"কীবোর্ড শর্টকাট"</string>
     <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"কীবোর্ড শর্টকাট কাস্টমাইজ করুন"</string>
     <string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"শর্টকাট সরাবেন?"</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"ডিফল্ট শর্টকার্ট আবার রিসেট করতে চান?"</string>
     <string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"শর্টকাট অ্যাসাইন করতে কী প্রেস করুন"</string>
     <string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"এটি আপনার কাস্টম শর্টকাট স্থায়ীভাবে মুছে ফেলবে।"</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"এটি আপনার সব কাস্টম শর্টকার্ট স্থায়ীভাবে মুছে দেবে।"</string>
     <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>
     <string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"অ্যাকশন বা মেটা কী আইকন"</string>
     <string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"প্লাস আইকন"</string>
     <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"কাস্টমাইজ করুন"</string>
-    <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
-    <skip />
+    <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"রিসেট করুন"</string>
     <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"হয়ে গেছে"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"আইকন বড় করুন"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"অথবা"</string>
@@ -1453,8 +1451,7 @@
     <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"কীবোর্ড সেটিংস"</string>
     <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"শর্টকাট সেট করুন"</string>
     <string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"সরান"</string>
-    <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
-    <skip />
+    <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"হ্যাঁ, রিসেট করতে চাই"</string>
     <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"বাতিল করুন"</string>
     <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"কী প্রেস করুন"</string>
     <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"কী কম্বিনেশন আগে থেকে ব্যবহার হচ্ছে। অন্য কী ব্যবহার করে দেখুন।"</string>
@@ -1486,8 +1483,7 @@
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"আপনার কীবোর্ডে অ্যাকশন কী প্রেস করুন"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"দারুণ!"</string>
     <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"আপনি \'সব অ্যাপের জেসচার দেখুন\' টিউটোরিয়াল সম্পূর্ণ করেছেন"</string>
-    <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
-    <skip />
+    <string name="tutorial_animation_content_description" msgid="2698816574982370184">"টিউটোরিয়াল অ্যানিমেশন পজ করুন এবং আবার চালু করতে ক্লিক করুন।"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"কীবোর্ড ব্যাকলাইট"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d-এর মধ্যে %1$d লেভেল"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"হোম কন্ট্রোল"</string>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index fe38ba8..f842aab 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -529,8 +529,9 @@
     <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>
     <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Vidžeti"</string>
-    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Da biste dodali prečac Widgeti, provjerite je li u postavkama omogućena opcija Prikaži widgete na zaključanom zaslonu."</string>
+    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Da dodate prečicu \"Vidžeti\", provjerite je li u postavkama omogućeno \"Prikazuj vidžete na zaključanom ekranu\"."</string>
     <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Postavke"</string>
+    <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Prikaži gumb čuvara zaslona"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Zamijeni korisnika"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"padajući meni"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Sve aplikacije i podaci iz ove sesije će se izbrisati."</string>
@@ -591,8 +592,7 @@
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Obavještenja"</string>
     <string name="notification_section_header_conversations" msgid="821834744538345661">"Razgovori"</string>
     <string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Obriši sva nečujna obavještenja"</string>
-    <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
-    <skip />
+    <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Otvaranje postavki obavještenja"</string>
     <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Obavještenja su pauzirana načinom rada Ne ometaj"</string>
     <string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Nema obavještenja}=1{Obavještenja su pauzirana putem načina rada {mode}}=2{Obavještenja su pauzirana putem načina rada {mode} i još jednog načina rada}one{Obavještenja su pauzirana putem načina rada {mode} i još # načina rada}few{Obavještenja su pauzirana putem načina rada {mode} i još # načina rada}other{Obavještenja su pauzirana putem načina rada {mode} i još # načina rada}}"</string>
     <string name="media_projection_action_text" msgid="3634906766918186440">"Započni odmah"</string>
@@ -707,8 +707,7 @@
     <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Praćenje položaja glave"</string>
     <string name="volume_ringer_change" msgid="3574969197796055532">"Dodirnite da promijenite način rada zvuka zvona"</string>
     <string name="volume_ringer_mode" msgid="6867838048430807128">"način rada za zvuk zvona"</string>
-    <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
-    <skip />
+    <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>; promjena načina rada zvuka zvona dodirom"</string>
     <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"isključite zvuk"</string>
     <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"uključite zvuk"</string>
     <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibriranje"</string>
@@ -873,9 +872,12 @@
     <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>
-    <string name="system_multitasking_lhs" msgid="8402954791206308783">"Korištenje podijeljenog ekrana s trenutnom aplikacijom na lijevoj strani"</string>
-    <string name="system_multitasking_full_screen" msgid="336048080383640562">"Prebacivanje s podijeljenog ekrana na prikaz preko cijelog ekrana"</string>
+    <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+    <skip />
+    <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+    <skip />
+    <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+    <skip />
     <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Prelazak u aplikaciju desno ili ispod uz podijeljeni ekran"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Pređite u aplikaciju lijevo ili iznad dok koristite podijeljeni ekran"</string>
     <string name="system_multitasking_replace" msgid="7410071959803642125">"Za vrijeme podijeljenog ekrana: zamjena jedne aplikacije drugom"</string>
@@ -1428,18 +1430,17 @@
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Prečice tastature"</string>
     <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Prilagodite prečice na tastaturi"</string>
     <string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Ukloniti prečicu?"</string>
-    <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Želite li vratiti na zadano?"</string>
+    <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Vratiti na zadano?"</string>
     <string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Pritisnite tipku da dodijelite prečicu"</string>
     <string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Ovo će trajno izbrisati prilagođenu prečicu."</string>
-    <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Time će se trajno izbrisati svi vaši prilagođeni prečaci."</string>
+    <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Ovo će trajno izbrisati sve vaše prilagođene prečice."</string>
     <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>
     <string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Ikona tipke radnji ili meta tipka"</string>
     <string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Ikona znaka plus"</string>
     <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Prilagođavanje"</string>
-    <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
-    <skip />
+    <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Poništavanje"</string>
     <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Gotovo"</string>
     <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>
@@ -1481,8 +1482,7 @@
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Pritisnite tipku radnji na tastaturi"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Odlično!"</string>
     <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Izvršili ste pokret za prikaz svih aplikacija"</string>
-    <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
-    <skip />
+    <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Animacija vodiča; pauziranje i nastavak reprodukcije klikom."</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Pozadinsko osvjetljenje tastature"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"%1$d. nivo od %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Kontrole za dom"</string>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index ba3c4ad..3386dc0 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -531,6 +531,7 @@
     <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgets"</string>
     <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Per afegir la drecera Widgets, assegura\'t que l\'opció Mostra els widgets a la pantalla de bloqueig estigui activada a la configuració."</string>
     <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Configuració"</string>
+    <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Botó Mostra l\'estalvi de pantalla"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Canvia d\'usuari"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menú desplegable"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Totes les aplicacions i les dades d\'aquesta sessió se suprimiran."</string>
@@ -591,8 +592,7 @@
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Notificacions"</string>
     <string name="notification_section_header_conversations" msgid="821834744538345661">"Converses"</string>
     <string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Esborra totes les notificacions silencioses"</string>
-    <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
-    <skip />
+    <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Obre la configuració de notificacions"</string>
     <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Notificacions pausades pel mode No molestis"</string>
     <string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{No hi ha cap notificació}=1{{mode} ha posat en pausa les notificacions}=2{{mode} i un altre mode han posat en pausa les notificacions}many{{mode} i # de modes més han posat en pausa les notificacions}other{{mode} i # modes més han posat en pausa les notificacions}}"</string>
     <string name="media_projection_action_text" msgid="3634906766918186440">"Comença ara"</string>
@@ -707,8 +707,7 @@
     <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Seguiment del cap"</string>
     <string name="volume_ringer_change" msgid="3574969197796055532">"Toca per canviar el mode de timbre"</string>
     <string name="volume_ringer_mode" msgid="6867838048430807128">"mode de timbre"</string>
-    <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
-    <skip />
+    <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>; toca per canviar el mode de timbre."</string>
     <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"silenciar"</string>
     <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"deixar de silenciar"</string>
     <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrar"</string>
@@ -873,9 +872,12 @@
     <string name="group_system_lock_screen" msgid="7391191300363416543">"Bloqueja la pantalla"</string>
     <string name="group_system_quick_memo" msgid="3764560265935722903">"Crea una nota"</string>
     <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Multitasca"</string>
-    <string name="system_multitasking_rhs" msgid="8714224917276297810">"Utilitza la pantalla dividida amb l\'aplicació actual a la dreta"</string>
-    <string name="system_multitasking_lhs" msgid="8402954791206308783">"Utilitza la pantalla dividida amb l\'aplicació actual a l\'esquerra"</string>
-    <string name="system_multitasking_full_screen" msgid="336048080383640562">"Canvia de pantalla dividida a pantalla completa"</string>
+    <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+    <skip />
+    <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+    <skip />
+    <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+    <skip />
     <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Canvia a l\'aplicació de la dreta o de sota amb la pantalla dividida"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Canvia a l\'aplicació de l\'esquerra o de dalt amb la pantalla dividida"</string>
     <string name="system_multitasking_replace" msgid="7410071959803642125">"Durant el mode de pantalla dividida: substitueix una app per una altra"</string>
@@ -1438,8 +1440,7 @@
     <string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Icona de la tecla d\'acció o Meta"</string>
     <string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Icona del signe més"</string>
     <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Personalitza"</string>
-    <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
-    <skip />
+    <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Restableix"</string>
     <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Fet"</string>
     <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>
@@ -1481,8 +1482,7 @@
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Prem la tecla d\'acció al teclat"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Enhorabona!"</string>
     <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Has completat el gest per veure totes les aplicacions"</string>
-    <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
-    <skip />
+    <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Animació del tutorial; fes clic per posar en pausa i reprendre la reproducció."</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Retroil·luminació del teclat"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Nivell %1$d de %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Controls de la llar"</string>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index 317061f..c5aea85 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -529,10 +529,9 @@
     <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>
     <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgety"</string>
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
-    <skip />
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
-    <skip />
+    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Pokud chcete přidat zkratku Widgety, zapněte v nastavení možnost Zobrazovat widgety na obrazovce uzamčení."</string>
+    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Nastavení"</string>
+    <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Zobrazit tlačítko spořiče obrazovky"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Přepnout uživatele"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"rozbalovací nabídka"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Veškeré aplikace a data v této relaci budou vymazána."</string>
@@ -593,8 +592,7 @@
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Oznámení"</string>
     <string name="notification_section_header_conversations" msgid="821834744538345661">"Konverzace"</string>
     <string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Vymazat všechna tichá oznámení"</string>
-    <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
-    <skip />
+    <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Otevřít nastavení oznámení"</string>
     <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Oznámení jsou pozastavena režimem Nerušit"</string>
     <string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Žádná oznámení}=1{Oznámení jsou pozastavená režimem {mode}}=2{Oznámení jsou pozastavená režimem {mode} a 1 dalším}few{Oznámení jsou pozastavená režimem {mode} a # dalšími}many{Oznámení jsou pozastavená režimem {mode} a # dalšího}other{Oznámení jsou pozastavená režimem {mode} a # dalšími}}"</string>
     <string name="media_projection_action_text" msgid="3634906766918186440">"Spustit"</string>
@@ -709,8 +707,7 @@
     <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Sledování hlavy"</string>
     <string name="volume_ringer_change" msgid="3574969197796055532">"Klepnutím změníte režim vyzvánění"</string>
     <string name="volume_ringer_mode" msgid="6867838048430807128">"režim vyzvánění"</string>
-    <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
-    <skip />
+    <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, klepnutím změníte režim vyzvánění"</string>
     <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"vypnout zvuk"</string>
     <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"zapnout zvuk"</string>
     <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrovat"</string>
@@ -875,9 +872,12 @@
     <string name="group_system_lock_screen" msgid="7391191300363416543">"Uzamknout obrazovku"</string>
     <string name="group_system_quick_memo" msgid="3764560265935722903">"Vytvořit poznámku"</string>
     <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Multitasking"</string>
-    <string name="system_multitasking_rhs" msgid="8714224917276297810">"Použít rozdělenou obrazovku se stávající aplikací vpravo"</string>
-    <string name="system_multitasking_lhs" msgid="8402954791206308783">"Použít rozdělenou obrazovku se stávající aplikací vlevo"</string>
-    <string name="system_multitasking_full_screen" msgid="336048080383640562">"Přepnout z rozdělené obrazovky na celou obrazovku"</string>
+    <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+    <skip />
+    <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+    <skip />
+    <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+    <skip />
     <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Přepnout na aplikaci vpravo nebo dole v režimu rozdělené obrazovky"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Přepnout na aplikaci vlevo nebo nahoře v režimu rozdělené obrazovky"</string>
     <string name="system_multitasking_replace" msgid="7410071959803642125">"V režimu rozdělené obrazovky: nahradit jednu aplikaci druhou"</string>
@@ -1430,20 +1430,17 @@
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Klávesové zkratky"</string>
     <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Přizpůsobení klávesových zkratek"</string>
     <string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Odstrabit zkratku?"</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Resetovat do výchozího nastavení?"</string>
     <string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Nastavte zkratku stisknutím klávesy"</string>
     <string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Vlastní zkratka se trvale smaže."</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Tím trvale odstraníte všechny své vlastní zkratky."</string>
     <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>
     <string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Ikona klávesy Akce nebo Meta"</string>
     <string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Ikona Plus"</string>
     <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Přizpůsobit"</string>
-    <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
-    <skip />
+    <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Resetovat"</string>
     <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Hotovo"</string>
     <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>
@@ -1453,8 +1450,7 @@
     <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Nastavení klávesnice"</string>
     <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Nastavit zkratku"</string>
     <string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Odstranit"</string>
-    <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
-    <skip />
+    <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Ano, resetovat"</string>
     <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Zrušit"</string>
     <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Stiskněte klávesu"</string>
     <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Kombinace kláves se už používá. Použijte jinou klávesu."</string>
@@ -1486,8 +1482,7 @@
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Stiskněte akční klávesu na klávesnici"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Výborně!"</string>
     <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Provedli jste gesto k zobrazení všech aplikací"</string>
-    <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
-    <skip />
+    <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Výuková animace, kliknutím pozastavíte nebo obnovíte."</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Podsvícení klávesnice"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Úroveň %1$d z %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Ovládání domácnosti"</string>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 3c0dc42..168afad 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -529,9 +529,9 @@
     <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>
     <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgets"</string>
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
-    <skip />
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
+    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Hvis du vil tilføje genvejen \"Widgets\", skal du sørge for, at \"Vis widgets på låseskærmen\" er aktiveret i indstillingerne."</string>
+    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Indstillinger"</string>
+    <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
     <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Skift bruger"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"rullemenu"</string>
@@ -593,8 +593,7 @@
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Notifikationer"</string>
     <string name="notification_section_header_conversations" msgid="821834744538345661">"Samtaler"</string>
     <string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Ryd alle lydløse notifikationer"</string>
-    <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
-    <skip />
+    <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Åbn indstillinger for notifikationer"</string>
     <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Notifikationer er sat på pause af Forstyr ikke"</string>
     <string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Ingen notifikationer}=1{Notifikationer er sat på pause af {mode}}=2{Notifikationer er sat på pause af {mode} og én anden tilstand}one{Notifikationer er sat på pause af {mode} og # anden tilstand}other{Notifikationer er sat på pause af {mode} og # andre tilstande}}"</string>
     <string name="media_projection_action_text" msgid="3634906766918186440">"Start nu"</string>
@@ -709,8 +708,7 @@
     <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Hovedregistrering"</string>
     <string name="volume_ringer_change" msgid="3574969197796055532">"Tryk for at ændre ringetilstand"</string>
     <string name="volume_ringer_mode" msgid="6867838048430807128">"ringetilstand"</string>
-    <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
-    <skip />
+    <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, tryk for at ændre ringetilstand"</string>
     <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"slå lyden fra"</string>
     <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"slå lyden til"</string>
     <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrer"</string>
@@ -875,9 +873,12 @@
     <string name="group_system_lock_screen" msgid="7391191300363416543">"Lås skærm"</string>
     <string name="group_system_quick_memo" msgid="3764560265935722903">"Skriv en note"</string>
     <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Multitasking"</string>
-    <string name="system_multitasking_rhs" msgid="8714224917276297810">"Brug opdelt skærm med aktuel app til højre"</string>
-    <string name="system_multitasking_lhs" msgid="8402954791206308783">"Brug opdelt skærm med aktuel app til venstre"</string>
-    <string name="system_multitasking_full_screen" msgid="336048080383640562">"Skift fra opdelt skærm til fuld skærm"</string>
+    <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+    <skip />
+    <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+    <skip />
+    <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+    <skip />
     <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Skift til en app til højre eller nedenfor, når du bruger opdelt skærm"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Skift til en app til venstre eller ovenfor, når du bruger opdelt skærm"</string>
     <string name="system_multitasking_replace" msgid="7410071959803642125">"Ved opdelt skærm: Udskift én app med en anden"</string>
@@ -1430,20 +1431,17 @@
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Tastaturgenveje"</string>
     <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Tilpas tastaturgenveje"</string>
     <string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Skal genvejen fjernes?"</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Vil du nulstille til standard?"</string>
     <string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Tryk på en tast for at tildele genvej"</string>
     <string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Denne handling sletter din tilpassede genvej permanent."</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Denne handling sletter alle dine tilpassede genveje permanent."</string>
     <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>
     <string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Ikon for handlingstast eller metatast"</string>
     <string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Plusikon"</string>
     <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Tilpas"</string>
-    <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
-    <skip />
+    <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Nulstil"</string>
     <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Udfør"</string>
     <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>
@@ -1453,8 +1451,7 @@
     <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Tastaturindstillinger"</string>
     <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Konfigurer genvej"</string>
     <string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Fjern"</string>
-    <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
-    <skip />
+    <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Ja, nulstil"</string>
     <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Annuller"</string>
     <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Tryk på en tast"</string>
     <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Tastekombinationen er allerede i brug. Prøv en anden tast."</string>
@@ -1486,8 +1483,7 @@
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Tryk på handlingstasten på dit tastatur"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Flot klaret!"</string>
     <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Du har udført bevægelsen for at se alle apps"</string>
-    <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
-    <skip />
+    <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Animation med vejledning. Klik for at sætte afspilningen på pause og genoptage den."</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Tastaturets baggrundslys"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Niveau %1$d af %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Hjemmestyring"</string>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 8d13524..bf323dd 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -529,9 +529,9 @@
     <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>
     <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgets"</string>
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
-    <skip />
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
+    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Zum Hinzufügen der Verknüpfung „Widgets“ musst du zuerst in den Einstellungen die Option „Widgets auf Sperrbildschirm zeigen“ aktivieren."</string>
+    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Einstellungen"</string>
+    <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
     <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Nutzer wechseln"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"Pull-down-Menü"</string>
@@ -593,8 +593,7 @@
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Benachrichtigungen"</string>
     <string name="notification_section_header_conversations" msgid="821834744538345661">"Unterhaltungen"</string>
     <string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Alle lautlosen Benachrichtigungen löschen"</string>
-    <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
-    <skip />
+    <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Benachrichtigungseinstellungen öffnen"</string>
     <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Benachrichtigungen durch „Bitte nicht stören“ pausiert"</string>
     <string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Keine Benachrichtigungen}=1{Benachrichtigungen durch {mode} pausiert}=2{Benachrichtigungen durch {mode} und einen weiteren Modus pausiert}other{Benachrichtigungen durch {mode} und # weitere Modi pausiert}}"</string>
     <string name="media_projection_action_text" msgid="3634906766918186440">"Jetzt starten"</string>
@@ -709,8 +708,7 @@
     <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Erfassung von Kopfbewe­gungen"</string>
     <string name="volume_ringer_change" msgid="3574969197796055532">"Zum Ändern des Klingeltonmodus tippen"</string>
     <string name="volume_ringer_mode" msgid="6867838048430807128">"Klingeltonmodus"</string>
-    <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
-    <skip />
+    <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, zum Ändern des Klingeltonmodus tippen"</string>
     <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"Stummschalten"</string>
     <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"Aufheben der Stummschaltung"</string>
     <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"Vibrieren lassen"</string>
@@ -875,9 +873,12 @@
     <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>
-    <string name="system_multitasking_lhs" msgid="8402954791206308783">"Splitscreen mit der aktuellen App auf der linken Seite nutzen"</string>
-    <string name="system_multitasking_full_screen" msgid="336048080383640562">"Vom Splitscreen zum Vollbild wechseln"</string>
+    <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+    <skip />
+    <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+    <skip />
+    <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+    <skip />
     <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Im Splitscreen-Modus zu einer App rechts oder unten wechseln"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Im Splitscreen-Modus zu einer App links oder oben wechseln"</string>
     <string name="system_multitasking_replace" msgid="7410071959803642125">"Im Splitscreen: eine App durch eine andere ersetzen"</string>
@@ -1430,20 +1431,17 @@
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Tastenkürzel"</string>
     <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Tastenkombinationen anpassen"</string>
     <string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Tastenkombination entfernen?"</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Auf Standardeinstellung zurücksetzen?"</string>
     <string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Drücke eine Taste, um eine Tastenkombination festzulegen"</string>
     <string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Dadurch wird die benutzerdefinierte Tastenkombination endgültig gelöscht."</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Dadurch werden alle deine benutzerdefinierten Tastenkombinationen endgültig gelöscht."</string>
     <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>
     <string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Symbol für Aktions- oder Meta-Taste"</string>
     <string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Plussymbol"</string>
     <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Anpassen"</string>
-    <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
-    <skip />
+    <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Zurücksetzen"</string>
     <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Fertig"</string>
     <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>
@@ -1453,8 +1451,7 @@
     <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Tastatureinstellungen"</string>
     <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Tastenkombination festlegen"</string>
     <string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Entfernen"</string>
-    <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
-    <skip />
+    <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Ja, zurücksetzen"</string>
     <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Abbrechen"</string>
     <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Taste drücken"</string>
     <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Diese Tastenkombination wird bereits verwendet. Versuche es mit einer anderen Taste."</string>
@@ -1486,8 +1483,7 @@
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Drücke die Aktionstaste auf deiner Tastatur"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Perfekt!"</string>
     <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Du hast das Tutorial für die Touch-Geste zum Aufrufen aller Apps abgeschlossen"</string>
-    <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
-    <skip />
+    <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Animation während des Tutorials, zum Pausieren und Fortsetzen der Wiedergabe klicken."</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Tastaturbeleuchtung"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Level %1$d von %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Smart-Home-Steuerung"</string>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index b6de539..a90f701 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -529,10 +529,9 @@
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Για να ανοίξετε μια εφαρμογή χρησιμοποιώντας ένα γραφικό στοιχείο, θα πρέπει να επαληθεύσετε την ταυτότητά σας. Επίσης, λάβετε υπόψη ότι η προβολή τους είναι δυνατή από οποιονδήποτε, ακόμα και όταν το tablet σας είναι κλειδωμένο. Ορισμένα γραφικά στοιχεία μπορεί να μην προορίζονται για την οθόνη κλειδώματος και η προσθήκη τους εδώ ενδέχεται να μην είναι ασφαλής."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Το κατάλαβα"</string>
     <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgets"</string>
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
-    <skip />
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
-    <skip />
+    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Για να προσθέσετε τη συντόμευση Γραφικά στοιχεία, βεβαιωθείτε ότι η ρύθμιση Εμφάνιση γραφικών στοιχείων στην οθόνη κλειδώματος είναι ενεργοποιημένη στις ρυθμίσεις."</string>
+    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Ρυθμίσεις"</string>
+    <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Εμφάνιση κουμπιού προφύλαξης οθόνης"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Εναλλαγή χρήστη"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"αναπτυσσόμενο μενού"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Όλες οι εφαρμογές και τα δεδομένα αυτής της περιόδου σύνδεσης θα διαγραφούν."</string>
@@ -593,8 +592,7 @@
     <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>
-    <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
-    <skip />
+    <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Άνοιγμα ρυθμίσεων ειδοποιήσεων"</string>
     <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Οι ειδοποιήσεις τέθηκαν σε παύση από τη λειτουργία \"Μην ενοχλείτε\""</string>
     <string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Δεν υπάρχουν ειδοποιήσεις}=1{Οι ειδοποιήσεις τέθηκαν σε παύση από τη λειτουργία {mode}}=2{Οι ειδοποιήσεις τέθηκαν σε παύση από τη λειτουργία {mode} και μία άλλη λειτουργία}other{Οι ειδοποιήσεις τέθηκαν σε παύση από τη λειτουργία {mode} και # άλλες λειτουργίες}}"</string>
     <string name="media_projection_action_text" msgid="3634906766918186440">"Έναρξη τώρα"</string>
@@ -709,8 +707,7 @@
     <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Παρακ. κίνησ. κεφαλής"</string>
     <string name="volume_ringer_change" msgid="3574969197796055532">"Πατήστε για να αλλάξετε τη λειτουργία ειδοποίησης ήχου"</string>
     <string name="volume_ringer_mode" msgid="6867838048430807128">"λειτουργία ειδοποίησης ήχου"</string>
-    <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
-    <skip />
+    <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, πατήστε για αλλαγή της λειτουργίας ειδοποίησης ήχου"</string>
     <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"σίγαση"</string>
     <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"κατάργηση σίγασης"</string>
     <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"δόνηση"</string>
@@ -875,9 +872,12 @@
     <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_full_screen" msgid="336048080383640562">"Εναλλαγή από διαχωρισμό οθόνης σε πλήρη οθόνη"</string>
+    <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+    <skip />
+    <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+    <skip />
+    <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+    <skip />
     <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>
@@ -1430,20 +1430,17 @@
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Συντομεύσεις πληκτρολογίου"</string>
     <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Προσαρμογή συντομεύσεων πληκτρολογίου"</string>
     <string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Κατάργηση συντόμευσης;"</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Επαναφορά στις προεπιλογές;"</string>
     <string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Πατήστε το πλήκτρο για ανάθεση της συντόμευσης"</string>
     <string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Με αυτή την ενέργεια, η προσαρμοσμένη συντόμευση θα διαγραφεί οριστικά."</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Με αυτή την ενέργεια θα διαγραφούν οριστικά όλες οι προσαρμοσμένες συντομεύσεις."</string>
     <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>
     <string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Εικονίδιο πλήκτρου ενέργειας ή Meta"</string>
     <string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Εικονίδιο συν"</string>
     <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Προσαρμογή"</string>
-    <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
-    <skip />
+    <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Επαναφορά"</string>
     <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Τέλος"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Εικονίδιο ανάπτυξης"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ή"</string>
@@ -1453,8 +1450,7 @@
     <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Ρυθμίσεις πληκτρολογίου"</string>
     <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Ορισμός συντόμευσης"</string>
     <string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Κατάργηση"</string>
-    <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
-    <skip />
+    <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Ναι, επαναφορά"</string>
     <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Ακύρωση"</string>
     <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Πατήστε ένα πλήκτρο"</string>
     <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Ο συνδυασμός πλήκτρων χρησιμοποιείται ήδη. Δοκιμάστε άλλο πλήκτρο."</string>
@@ -1486,8 +1482,7 @@
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Πατήστε το πλήκτρο ενέργειας στο πληκτρολόγιό σας"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Μπράβο!"</string>
     <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Ολοκληρώσατε την κίνηση για την προβολή όλων των εφαρμογών"</string>
-    <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
-    <skip />
+    <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Κινούμενη εικόνα οδηγού, κάντε κλικ για παύση και συνέχιση της αναπαραγωγής."</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Οπίσθιος φωτισμός πληκτρολογίου"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Επίπεδο %1$d από %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Οικιακοί έλεγχοι"</string>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index 609e7af..679684a 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -529,9 +529,9 @@
     <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>
     <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgets"</string>
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
-    <skip />
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
+    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"To add the \'Widgets\' shortcut, make sure that \'Show widgets on lock screen\' is enabled in settings."</string>
+    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Settings"</string>
+    <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
     <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Switch user"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"pulldown menu"</string>
@@ -593,8 +593,7 @@
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Notifications"</string>
     <string name="notification_section_header_conversations" msgid="821834744538345661">"Conversations"</string>
     <string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Clear all silent notifications"</string>
-    <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
-    <skip />
+    <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Open notifications settings"</string>
     <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Notifications paused by Do Not Disturb"</string>
     <string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{No notifications}=1{Notifications paused by {mode}}=2{Notifications paused by {mode} and one other mode}other{Notifications paused by {mode} and # other modes}}"</string>
     <string name="media_projection_action_text" msgid="3634906766918186440">"Start now"</string>
@@ -709,8 +708,7 @@
     <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Head tracking"</string>
     <string name="volume_ringer_change" msgid="3574969197796055532">"Tap to change ringer mode"</string>
     <string name="volume_ringer_mode" msgid="6867838048430807128">"ringer mode"</string>
-    <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
-    <skip />
+    <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, tap to change ringer mode"</string>
     <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"mute"</string>
     <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"unmute"</string>
     <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrate"</string>
@@ -875,9 +873,12 @@
     <string name="group_system_lock_screen" msgid="7391191300363416543">"Lock screen"</string>
     <string name="group_system_quick_memo" msgid="3764560265935722903">"Take a note"</string>
     <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Multi-tasking"</string>
-    <string name="system_multitasking_rhs" msgid="8714224917276297810">"Use split screen with current app on the right"</string>
-    <string name="system_multitasking_lhs" msgid="8402954791206308783">"Use split screen with current app on the left"</string>
-    <string name="system_multitasking_full_screen" msgid="336048080383640562">"Switch from split screen to full screen"</string>
+    <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+    <skip />
+    <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+    <skip />
+    <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+    <skip />
     <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Switch to the app on the right or below while using split screen"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Switch to the app on the left or above while using split screen"</string>
     <string name="system_multitasking_replace" msgid="7410071959803642125">"During split screen: Replace an app from one to another"</string>
@@ -1430,20 +1431,17 @@
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Keyboard shortcuts"</string>
     <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Customise keyboard shortcuts"</string>
     <string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Remove shortcut?"</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Reset back to default?"</string>
     <string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Press key to assign shortcut"</string>
     <string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"This will delete your custom shortcut permanently."</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"This will delete all your custom shortcuts permanently."</string>
     <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>
     <string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Action or Meta key icon"</string>
     <string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Plus icon"</string>
     <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Customise"</string>
-    <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
-    <skip />
+    <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Reset"</string>
     <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Done"</string>
     <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>
@@ -1453,8 +1451,7 @@
     <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Keyboard settings"</string>
     <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Set shortcut"</string>
     <string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Remove"</string>
-    <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
-    <skip />
+    <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Yes, reset"</string>
     <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Cancel"</string>
     <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Press key"</string>
     <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Key combination already in use. Try another key."</string>
@@ -1486,8 +1483,7 @@
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Press the action key on your keyboard"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Well done!"</string>
     <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"You completed the view all apps gesture"</string>
-    <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
-    <skip />
+    <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Tutorial animation, click to pause and resume play."</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Keyboard backlight"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Level %1$d of %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Home controls"</string>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index be4e81f..f54745d 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -531,6 +531,7 @@
     <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgets"</string>
     <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"To add the \"Widgets\" shortcut, make sure \"Show widgets on lock screen\" is enabled in settings."</string>
     <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Settings"</string>
+    <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Show screensaver button"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Switch user"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"pulldown menu"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"All apps and data in this session will be deleted."</string>
@@ -871,9 +872,9 @@
     <string name="group_system_lock_screen" msgid="7391191300363416543">"Lock screen"</string>
     <string name="group_system_quick_memo" msgid="3764560265935722903">"Take a note"</string>
     <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Multitasking"</string>
-    <string name="system_multitasking_rhs" msgid="8714224917276297810">"Use split screen with current app on the right"</string>
-    <string name="system_multitasking_lhs" msgid="8402954791206308783">"Use split screen with current app on the left"</string>
-    <string name="system_multitasking_full_screen" msgid="336048080383640562">"Switch from split screen to full screen"</string>
+    <string name="system_multitasking_rhs" msgid="8779289852395243004">"Use split screen with app on the right"</string>
+    <string name="system_multitasking_lhs" msgid="7348595296208696452">"Use split screen with app on the left"</string>
+    <string name="system_multitasking_full_screen" msgid="4940465971687159429">"Switch to full screen"</string>
     <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Switch to app on right or below while using split screen"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Switch to app on left or above while using split screen"</string>
     <string name="system_multitasking_replace" msgid="7410071959803642125">"During split screen: replace an app from one to another"</string>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index 609e7af..679684a 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -529,9 +529,9 @@
     <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>
     <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgets"</string>
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
-    <skip />
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
+    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"To add the \'Widgets\' shortcut, make sure that \'Show widgets on lock screen\' is enabled in settings."</string>
+    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Settings"</string>
+    <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
     <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Switch user"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"pulldown menu"</string>
@@ -593,8 +593,7 @@
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Notifications"</string>
     <string name="notification_section_header_conversations" msgid="821834744538345661">"Conversations"</string>
     <string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Clear all silent notifications"</string>
-    <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
-    <skip />
+    <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Open notifications settings"</string>
     <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Notifications paused by Do Not Disturb"</string>
     <string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{No notifications}=1{Notifications paused by {mode}}=2{Notifications paused by {mode} and one other mode}other{Notifications paused by {mode} and # other modes}}"</string>
     <string name="media_projection_action_text" msgid="3634906766918186440">"Start now"</string>
@@ -709,8 +708,7 @@
     <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Head tracking"</string>
     <string name="volume_ringer_change" msgid="3574969197796055532">"Tap to change ringer mode"</string>
     <string name="volume_ringer_mode" msgid="6867838048430807128">"ringer mode"</string>
-    <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
-    <skip />
+    <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, tap to change ringer mode"</string>
     <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"mute"</string>
     <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"unmute"</string>
     <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrate"</string>
@@ -875,9 +873,12 @@
     <string name="group_system_lock_screen" msgid="7391191300363416543">"Lock screen"</string>
     <string name="group_system_quick_memo" msgid="3764560265935722903">"Take a note"</string>
     <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Multi-tasking"</string>
-    <string name="system_multitasking_rhs" msgid="8714224917276297810">"Use split screen with current app on the right"</string>
-    <string name="system_multitasking_lhs" msgid="8402954791206308783">"Use split screen with current app on the left"</string>
-    <string name="system_multitasking_full_screen" msgid="336048080383640562">"Switch from split screen to full screen"</string>
+    <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+    <skip />
+    <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+    <skip />
+    <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+    <skip />
     <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Switch to the app on the right or below while using split screen"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Switch to the app on the left or above while using split screen"</string>
     <string name="system_multitasking_replace" msgid="7410071959803642125">"During split screen: Replace an app from one to another"</string>
@@ -1430,20 +1431,17 @@
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Keyboard shortcuts"</string>
     <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Customise keyboard shortcuts"</string>
     <string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Remove shortcut?"</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Reset back to default?"</string>
     <string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Press key to assign shortcut"</string>
     <string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"This will delete your custom shortcut permanently."</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"This will delete all your custom shortcuts permanently."</string>
     <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>
     <string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Action or Meta key icon"</string>
     <string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Plus icon"</string>
     <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Customise"</string>
-    <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
-    <skip />
+    <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Reset"</string>
     <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Done"</string>
     <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>
@@ -1453,8 +1451,7 @@
     <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Keyboard settings"</string>
     <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Set shortcut"</string>
     <string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Remove"</string>
-    <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
-    <skip />
+    <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Yes, reset"</string>
     <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Cancel"</string>
     <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Press key"</string>
     <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Key combination already in use. Try another key."</string>
@@ -1486,8 +1483,7 @@
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Press the action key on your keyboard"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Well done!"</string>
     <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"You completed the view all apps gesture"</string>
-    <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
-    <skip />
+    <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Tutorial animation, click to pause and resume play."</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Keyboard backlight"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Level %1$d of %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Home controls"</string>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index 609e7af..679684a 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -529,9 +529,9 @@
     <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>
     <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgets"</string>
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
-    <skip />
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
+    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"To add the \'Widgets\' shortcut, make sure that \'Show widgets on lock screen\' is enabled in settings."</string>
+    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Settings"</string>
+    <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
     <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Switch user"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"pulldown menu"</string>
@@ -593,8 +593,7 @@
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Notifications"</string>
     <string name="notification_section_header_conversations" msgid="821834744538345661">"Conversations"</string>
     <string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Clear all silent notifications"</string>
-    <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
-    <skip />
+    <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Open notifications settings"</string>
     <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Notifications paused by Do Not Disturb"</string>
     <string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{No notifications}=1{Notifications paused by {mode}}=2{Notifications paused by {mode} and one other mode}other{Notifications paused by {mode} and # other modes}}"</string>
     <string name="media_projection_action_text" msgid="3634906766918186440">"Start now"</string>
@@ -709,8 +708,7 @@
     <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Head tracking"</string>
     <string name="volume_ringer_change" msgid="3574969197796055532">"Tap to change ringer mode"</string>
     <string name="volume_ringer_mode" msgid="6867838048430807128">"ringer mode"</string>
-    <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
-    <skip />
+    <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, tap to change ringer mode"</string>
     <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"mute"</string>
     <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"unmute"</string>
     <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrate"</string>
@@ -875,9 +873,12 @@
     <string name="group_system_lock_screen" msgid="7391191300363416543">"Lock screen"</string>
     <string name="group_system_quick_memo" msgid="3764560265935722903">"Take a note"</string>
     <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Multi-tasking"</string>
-    <string name="system_multitasking_rhs" msgid="8714224917276297810">"Use split screen with current app on the right"</string>
-    <string name="system_multitasking_lhs" msgid="8402954791206308783">"Use split screen with current app on the left"</string>
-    <string name="system_multitasking_full_screen" msgid="336048080383640562">"Switch from split screen to full screen"</string>
+    <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+    <skip />
+    <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+    <skip />
+    <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+    <skip />
     <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Switch to the app on the right or below while using split screen"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Switch to the app on the left or above while using split screen"</string>
     <string name="system_multitasking_replace" msgid="7410071959803642125">"During split screen: Replace an app from one to another"</string>
@@ -1430,20 +1431,17 @@
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Keyboard shortcuts"</string>
     <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Customise keyboard shortcuts"</string>
     <string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Remove shortcut?"</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Reset back to default?"</string>
     <string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Press key to assign shortcut"</string>
     <string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"This will delete your custom shortcut permanently."</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"This will delete all your custom shortcuts permanently."</string>
     <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>
     <string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Action or Meta key icon"</string>
     <string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Plus icon"</string>
     <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Customise"</string>
-    <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
-    <skip />
+    <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Reset"</string>
     <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Done"</string>
     <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>
@@ -1453,8 +1451,7 @@
     <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Keyboard settings"</string>
     <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Set shortcut"</string>
     <string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Remove"</string>
-    <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
-    <skip />
+    <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Yes, reset"</string>
     <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Cancel"</string>
     <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Press key"</string>
     <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Key combination already in use. Try another key."</string>
@@ -1486,8 +1483,7 @@
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Press the action key on your keyboard"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Well done!"</string>
     <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"You completed the view all apps gesture"</string>
-    <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
-    <skip />
+    <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Tutorial animation, click to pause and resume play."</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Keyboard backlight"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Level %1$d of %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Home controls"</string>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 177e690..ca53976 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -529,10 +529,9 @@
     <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>
     <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgets"</string>
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
-    <skip />
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
-    <skip />
+    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Para agregar el acceso directo de \"Widgets\", asegúrate de que la opción \"Mostrar widgets en la pantalla de bloqueo\" esté habilitada en la configuración."</string>
+    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Configuración"</string>
+    <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Botón para mostrar el protector de pantalla"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Cambiar usuario"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menú expandible"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Se eliminarán las aplicaciones y los datos de esta sesión."</string>
@@ -593,8 +592,7 @@
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Notificaciones"</string>
     <string name="notification_section_header_conversations" msgid="821834744538345661">"Conversaciones"</string>
     <string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Borrar todas las notificaciones silenciosas"</string>
-    <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
-    <skip />
+    <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Abrir configuración de notificaciones"</string>
     <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Notificaciones pausadas por el modo \"No interrumpir\""</string>
     <string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{No hay notificaciones}=1{{mode} pausó las notificaciones}=2{{mode} y un modo más pausaron las notificaciones}many{{mode} y # de modos más pausaron las notificaciones}other{{mode} y # modos más pausaron las notificaciones}}"</string>
     <string name="media_projection_action_text" msgid="3634906766918186440">"Comenzar ahora"</string>
@@ -874,9 +872,12 @@
     <string name="group_system_lock_screen" msgid="7391191300363416543">"Bloquear la pantalla"</string>
     <string name="group_system_quick_memo" msgid="3764560265935722903">"Crear una nota"</string>
     <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Tareas múltiples"</string>
-    <string name="system_multitasking_rhs" msgid="8714224917276297810">"Usar la pantalla dividida con la app actual a la derecha"</string>
-    <string name="system_multitasking_lhs" msgid="8402954791206308783">"Usar la pantalla dividida con la app actual a la izquierda"</string>
-    <string name="system_multitasking_full_screen" msgid="336048080383640562">"Cambiar de pantalla dividida a pantalla completa"</string>
+    <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+    <skip />
+    <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+    <skip />
+    <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+    <skip />
     <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Ubicar la app a la derecha o abajo cuando usas la pantalla dividida"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Ubicar la app a la izquierda o arriba cuando usas la pantalla dividida"</string>
     <string name="system_multitasking_replace" msgid="7410071959803642125">"Durante pantalla dividida: Reemplaza una app con otra"</string>
@@ -1429,20 +1430,17 @@
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Combinaciones de teclas"</string>
     <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Personaliza las combinaciones de teclas"</string>
     <string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"¿Quieres quitar el acceso directo?"</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"¿Quieres restablecer la configuración predeterminada?"</string>
     <string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Presiona la tecla para asignar el acceso directo"</string>
     <string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Esta acción borrará tu acceso directo personalizado de forma permanente."</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Esta acción borrará todos tus accesos directos personalizados de forma permanente."</string>
     <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>
     <string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Ícono tecla meta o de acción"</string>
     <string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"ícono de signo más"</string>
     <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Personalizar"</string>
-    <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
-    <skip />
+    <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Restablecer"</string>
     <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Listo"</string>
     <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>
@@ -1452,8 +1450,7 @@
     <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Configuración del teclado"</string>
     <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Establecer combinación de teclas"</string>
     <string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Quitar"</string>
-    <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
-    <skip />
+    <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Sí, restablecer"</string>
     <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Cancelar"</string>
     <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Presiona una tecla"</string>
     <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"La combinación de teclas ya está en uso. Prueba con otra."</string>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 53289d1..28812cc 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -531,6 +531,8 @@
     <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgets"</string>
     <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Para añadir el acceso directo Widgets, asegúrate de que la opción Mostrar widgets en la pantalla de bloqueo esté habilitada en los ajustes."</string>
     <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Ajustes"</string>
+    <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
+    <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Cambiar de usuario"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menú desplegable"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Se eliminarán todas las aplicaciones y datos de esta sesión."</string>
@@ -591,8 +593,7 @@
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Notificaciones"</string>
     <string name="notification_section_header_conversations" msgid="821834744538345661">"Conversaciones"</string>
     <string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Borrar todas las notificaciones silenciosas"</string>
-    <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
-    <skip />
+    <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Abrir los ajustes de notificaciones"</string>
     <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Notificaciones pausadas por el modo No molestar"</string>
     <string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{No hay notificaciones}=1{Notificaciones pausadas por {mode}}=2{Notificaciones pausadas por {mode} y un modo más}many{Notificaciones pausadas por {mode} y # modos más}other{Notificaciones pausadas por {mode} y # modos más}}"</string>
     <string name="media_projection_action_text" msgid="3634906766918186440">"Empezar ahora"</string>
@@ -707,8 +708,7 @@
     <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Seguimiento de cabeza"</string>
     <string name="volume_ringer_change" msgid="3574969197796055532">"Toca para cambiar el modo de timbre"</string>
     <string name="volume_ringer_mode" msgid="6867838048430807128">"modo de timbre"</string>
-    <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
-    <skip />
+    <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, toca para cambiar el modo de timbre"</string>
     <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"silenciar"</string>
     <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"dejar de silenciar"</string>
     <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrar"</string>
@@ -873,9 +873,12 @@
     <string name="group_system_lock_screen" msgid="7391191300363416543">"Pantalla de bloqueo"</string>
     <string name="group_system_quick_memo" msgid="3764560265935722903">"Escribir una nota"</string>
     <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Multitarea"</string>
-    <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>
+    <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+    <skip />
+    <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+    <skip />
+    <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+    <skip />
     <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>
@@ -1438,8 +1441,7 @@
     <string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Icono de la tecla de acción o de la tecla Meta"</string>
     <string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Icono de más"</string>
     <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Personalizar"</string>
-    <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
-    <skip />
+    <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Restablecer"</string>
     <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Hecho"</string>
     <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>
@@ -1481,8 +1483,7 @@
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Pulsa la tecla de acción de tu teclado"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"¡Muy bien!"</string>
     <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Has completado el gesto para ver todas las aplicaciones"</string>
-    <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
-    <skip />
+    <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Animación del tutorial, haz clic para pausar y reanudar la reproducción."</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Retroiluminación del teclado"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Nivel %1$d de %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Controles de la casa"</string>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index 165f9c2..2c0c378 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -529,9 +529,9 @@
     <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>
     <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Vidinad"</string>
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
-    <skip />
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
+    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Otsetee „Vidinad“ lisamiseks veenduge, et seadetes oleks valik „Kuva lukustuskuval vidinad“ lubatud."</string>
+    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Seaded"</string>
+    <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
     <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Kasutaja vahetamine"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"rippmenüü"</string>
@@ -593,8 +593,7 @@
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Märguanded"</string>
     <string name="notification_section_header_conversations" msgid="821834744538345661">"Vestlused"</string>
     <string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Kustuta kõik hääletud märguanded"</string>
-    <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
-    <skip />
+    <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Avage märguandeseaded"</string>
     <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Režiim Mitte segada peatas märguanded"</string>
     <string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Märguandeid pole}=1{{mode} peatas märguanded}=2{{mode} ja veel üks režiim peatasid märguanded}other{{mode} ja veel # režiimi peatasid märguanded}}"</string>
     <string name="media_projection_action_text" msgid="3634906766918186440">"Alusta kohe"</string>
@@ -709,8 +708,7 @@
     <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Pea jälgimine"</string>
     <string name="volume_ringer_change" msgid="3574969197796055532">"Puudutage telefonihelina režiimi muutmiseks"</string>
     <string name="volume_ringer_mode" msgid="6867838048430807128">"telefonihelina režiim"</string>
-    <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
-    <skip />
+    <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, puudutage telefonihelina režiimi muutmiseks"</string>
     <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"vaigistamine"</string>
     <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"vaigistuse tühistamine"</string>
     <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibreerimine"</string>
@@ -875,9 +873,12 @@
     <string name="group_system_lock_screen" msgid="7391191300363416543">"Lukustuskuva"</string>
     <string name="group_system_quick_memo" msgid="3764560265935722903">"Märkme tegemine"</string>
     <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Multitegumtöö"</string>
-    <string name="system_multitasking_rhs" msgid="8714224917276297810">"Jagatud ekraanikuva kasutamine, praegune rakendus kuvatakse paremal"</string>
-    <string name="system_multitasking_lhs" msgid="8402954791206308783">"Jagatud ekraanikuva kasutamine, praegune rakendus kuvatakse vasakul"</string>
-    <string name="system_multitasking_full_screen" msgid="336048080383640562">"Jagatud ekraanikuvalt täisekraanile lülitamine"</string>
+    <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+    <skip />
+    <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+    <skip />
+    <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+    <skip />
     <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Paremale või alumisele rakendusele lülitamine jagatud ekraani ajal"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Vasakule või ülemisele rakendusele lülitamine jagatud ekraani ajal"</string>
     <string name="system_multitasking_replace" msgid="7410071959803642125">"Ekraanikuva jagamise ajal: ühe rakenduse asendamine teisega"</string>
@@ -1430,20 +1431,17 @@
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Klaviatuuri otseteed"</string>
     <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Klaviatuuri otseteede kohandamine"</string>
     <string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Kas soovite otsetee eemaldada?"</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Kas lähtestada vaikeseadele?"</string>
     <string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Otsetee lisamiseks vajutage klahvi"</string>
     <string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"See kustutab teie kohandatud otsetee jäädavalt."</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"See kustutab kõik teie kohandatud otseteed jäädavalt."</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Otsige otseteid"</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>
     <string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Toiming või metaklahv"</string>
     <string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Pluss-ikoon"</string>
     <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Kohandamine"</string>
-    <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
-    <skip />
+    <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Lähtesta"</string>
     <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Valmis"</string>
     <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>
@@ -1453,8 +1451,7 @@
     <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Klaviatuuri seaded"</string>
     <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Määrake otsetee"</string>
     <string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Eemalda"</string>
-    <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
-    <skip />
+    <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Jah, lähtesta"</string>
     <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Tühista"</string>
     <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Vajutage klahvi"</string>
     <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Klahvikombinatsioon on juba kasutusel. Proovige mõnda muud klahvi."</string>
@@ -1486,8 +1483,7 @@
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Vajutage klaviatuuril toiminguklahvi"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Hästi tehtud!"</string>
     <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Tegite kõigi rakenduste vaatamise liigutuse"</string>
-    <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
-    <skip />
+    <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Õpetlik animatsioon, klõpsake esitamise peatamiseks ja jätkamiseks."</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Klaviatuuri taustavalgustus"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Tase %1$d/%2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Kodu juhtelemendid"</string>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index 46bbdc3..cfd6c6a 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -529,9 +529,9 @@
     <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>
     <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgetak"</string>
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
-    <skip />
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
+    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"\"Widgetak\" lasterbidea gehitzeko, ziurtatu \"Erakutsi widgetak pantaila blokeatuan\" gaituta dagoela ezarpenetan."</string>
+    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Ezarpenak"</string>
+    <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
     <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Aldatu erabiltzailea"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"zabaldu menua"</string>
@@ -593,8 +593,7 @@
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Jakinarazpenak"</string>
     <string name="notification_section_header_conversations" msgid="821834744538345661">"Elkarrizketak"</string>
     <string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Garbitu soinurik gabeko jakinarazpen guztiak"</string>
-    <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
-    <skip />
+    <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Ireki jakinarazpen-ezarpenak"</string>
     <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Ez molestatzeko moduak pausatu egin ditu jakinarazpenak"</string>
     <string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Ez dago jakinarazpenik}=1{Modu honek jakinarazpenak pausatu ditu: {mode}}=2{Modu honek eta beste modu batek jakinarazpenak pausatu dituzte: {mode}}other{Modu honek eta beste # moduk jakinarazpenak pausatu dituzte: {mode}}}"</string>
     <string name="media_projection_action_text" msgid="3634906766918186440">"Hasi"</string>
@@ -709,8 +708,7 @@
     <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Buruaren jarraipena"</string>
     <string name="volume_ringer_change" msgid="3574969197796055532">"Sakatu tonu-jotzailearen modua aldatzeko"</string>
     <string name="volume_ringer_mode" msgid="6867838048430807128">"tonu-jotzailearen modua"</string>
-    <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
-    <skip />
+    <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>. Sakatu tonu-jotzailearen modua aldatzeko."</string>
     <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"desaktibatu audioa"</string>
     <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"aktibatu audioa"</string>
     <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"dardara"</string>
@@ -875,9 +873,12 @@
     <string name="group_system_lock_screen" msgid="7391191300363416543">"Blokeatu pantaila"</string>
     <string name="group_system_quick_memo" msgid="3764560265935722903">"Egin ohar bat"</string>
     <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Zeregin bat baino gehiago aldi berean exekutatzea"</string>
-    <string name="system_multitasking_rhs" msgid="8714224917276297810">"Erabili pantaila zatitua eta ezarri aplikazio hau eskuinean"</string>
-    <string name="system_multitasking_lhs" msgid="8402954791206308783">"Erabili pantaila zatitua eta ezarri aplikazio hau ezkerrean"</string>
-    <string name="system_multitasking_full_screen" msgid="336048080383640562">"Aldatu pantaila zatitutik pantaila osora"</string>
+    <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+    <skip />
+    <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+    <skip />
+    <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+    <skip />
     <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Aldatu eskuineko edo beheko aplikaziora pantaila zatitua erabiltzean"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Aldatu ezkerreko edo goiko aplikaziora pantaila zatitua erabiltzean"</string>
     <string name="system_multitasking_replace" msgid="7410071959803642125">"Pantaila zatituan zaudela, ordeztu aplikazio bat beste batekin"</string>
@@ -1430,20 +1431,17 @@
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Lasterbideak"</string>
     <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Pertsonalizatu lasterbideak"</string>
     <string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Lasterbidea kendu nahi duzu?"</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Balio lehenetsia berrezarri nahi duzu?"</string>
     <string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Sakatu tekla lasterbidea esleitzeko"</string>
     <string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Betiko ezabatuko da lasterbide pertsonalizatua."</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Lasterbide pertsonalizatu guztiak betiko ezabatuko dira."</string>
     <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>
     <string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Ekintzaren edo Meta teklaren ikonoa"</string>
     <string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Plus-ikonoa"</string>
     <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Pertsonalizatu"</string>
-    <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
-    <skip />
+    <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Berrezarri"</string>
     <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Eginda"</string>
     <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>
@@ -1453,8 +1451,7 @@
     <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Teklatuaren ezarpenak"</string>
     <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Ezarri lasterbidea"</string>
     <string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Kendu"</string>
-    <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
-    <skip />
+    <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Bai, berrezarri"</string>
     <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Utzi"</string>
     <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Sakatu tekla"</string>
     <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Tekla-konbinazio hori erabili da dagoeneko. Probatu beste tekla bat."</string>
@@ -1486,8 +1483,7 @@
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Sakatu teklatuko ekintza-tekla"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Bikain!"</string>
     <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Osatu duzu aplikazio guztiak ikusteko keinua"</string>
-    <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
-    <skip />
+    <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Tutorialeko animazioa. Sakatu pausatzeko eta erreproduzitzeari berrekiteko."</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Teklatuaren hondoko argia"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"%1$d/%2$d maila"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Etxeko gailuen kontrola"</string>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index 5e68a95c..163d84b 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -529,9 +529,9 @@
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"برای باز کردن برنامه بااستفاده از ابزاره، باید هویت خودتان را به‌تأیید برسانید. همچنین، به‌خاطر داشته باشید که همه می‌توانند آن‌ها را مشاهده کنند، حتی وقتی رایانه لوحی‌تان قفل است. برخی‌از ابزاره‌ها ممکن است برای صفحه قفل درنظر گرفته نشده باشند و ممکن است اضافه کردن آن‌ها در اینجا ناامن باشد."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"متوجه‌ام"</string>
     <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"ابزاره‌ها"</string>
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
-    <skip />
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
+    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"برای افزودن میان‌بر «ابزاره‌ها»، مطمئن شوید «نمایش ابزاره‌ها در صفحه قفل» در تنظیمات فعال باشد."</string>
+    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"تنظیمات"</string>
+    <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
     <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"تغییر کاربر"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"منوی پایین‌پر"</string>
@@ -593,8 +593,7 @@
     <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>
-    <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
-    <skip />
+    <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"باز کردن تنظیمات اعلان"</string>
     <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"اعلان‌ها توسط «مزاحم نشوید» موقتاً متوقف شدند"</string>
     <string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{اعلانی موقتاً متوقف نشده است}=1{اعلان‌ها را «{mode}» موقتاً متوقف کرده است}=2{اعلان‌ها را «{mode}» و یک حالت دیگر موقتاً متوقف کرد‌اند}one{اعلان‌ها را «{mode}» و # حالت دیگر موقتاً متوقف کرده‌اند}other{اعلان‌ها را «{mode}» و # حالت دیگر موقتاً متوقف کرده‌اند}}"</string>
     <string name="media_projection_action_text" msgid="3634906766918186440">"اکنون شروع کنید"</string>
@@ -709,8 +708,7 @@
     <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"ردیابی سر"</string>
     <string name="volume_ringer_change" msgid="3574969197796055532">"برای تغییر حالت زنگ، تک‌ضرب بزنید"</string>
     <string name="volume_ringer_mode" msgid="6867838048430807128">"حالت زنگ"</string>
-    <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
-    <skip />
+    <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>، برای تغییر حالت زنگ تک‌ضرب بزنید"</string>
     <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"صامت کردن"</string>
     <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"باصدا کردن"</string>
     <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"لرزش"</string>
@@ -875,9 +873,12 @@
     <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_full_screen" msgid="336048080383640562">"جابه‌جایی از صفحهٔ دونیمه به تمام صفحه"</string>
+    <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+    <skip />
+    <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+    <skip />
+    <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+    <skip />
     <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>
@@ -1430,20 +1431,17 @@
     <string name="shortcut_helper_title" msgid="8567500639300970049">"میان‌برهای صفحه‌کلید"</string>
     <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"سفارشی‌سازی کردن میان‌برهای صفحه‌کلید"</string>
     <string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"میان‌بر حذف شود؟"</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"به تنظیم پیش‌فرض بازنشانی می‌کنید؟"</string>
     <string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"برای اختصاص دادن میان‌بر، کلید را فشار دهید"</string>
     <string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"با این کار، میان‌بر سفارشی شما برای همیشه حذف می‌شود."</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"با این کار، همه میان‌برهای سفارشی برای همیشه حذف خواهند شد."</string>
     <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>
     <string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"نماد کلید کنش یا متا"</string>
     <string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"نماد جمع"</string>
     <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"سفارشی‌سازی کردن"</string>
-    <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
-    <skip />
+    <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"بازنشانی"</string>
     <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"تمام"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"نماد ازهم بازکردن"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"یا"</string>
@@ -1453,8 +1451,7 @@
     <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"تنظیمات صفحه‌کلید"</string>
     <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"تنظیم میان‌بر"</string>
     <string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"حذف"</string>
-    <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
-    <skip />
+    <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"بله، بازنشانی شود"</string>
     <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"لغو"</string>
     <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"کلید را فشار دهید"</string>
     <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"ترکیب کلید ازقبل درحال استفاده است. کلید دیگری را امتحان کنید."</string>
@@ -1486,8 +1483,7 @@
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"دکمه کنش را روی صفحه لمسی فشار دهید"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"عالی بود!"</string>
     <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"اشاره «مشاهده همه برنامه‌ها» را تمام کردید"</string>
-    <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
-    <skip />
+    <string name="tutorial_animation_content_description" msgid="2698816574982370184">"پویانمایی آموزش گام‌به‌گام، برای توقف موقت و ازسرگیری پخش کلیک کنید."</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"نور پس‌زمینه صفحه‌کلید"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"‏سطح %1$d از %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"کنترل خانه هوشمند"</string>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index 94b6364..1caac76 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -531,6 +531,8 @@
     <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgetit"</string>
     <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Jos haluat lisätä Widgetit-pikakuvakkeen, varmista, että \"Näytä widgetit lukitusnäytöllä\" on käytössä asetuksissa."</string>
     <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Asetukset"</string>
+    <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
+    <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Vaihda käyttäjää"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"alasvetovalikko"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Kaikki sovellukset ja tämän istunnon tiedot poistetaan."</string>
@@ -591,8 +593,7 @@
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Ilmoitukset"</string>
     <string name="notification_section_header_conversations" msgid="821834744538345661">"Keskustelut"</string>
     <string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Tyhjennä kaikki hiljaiset ilmoitukset"</string>
-    <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
-    <skip />
+    <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Avaa ilmoitusasetukset"</string>
     <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Älä häiritse ‑tila keskeytti ilmoitukset"</string>
     <string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Ei ilmoituksia}=1{{mode} keskeytti ilmoitukset}=2{{mode} ja yksi muu tila keskeytti ilmoitukset}other{{mode} ja # muuta tilaa keskeytti ilmoitukset}}"</string>
     <string name="media_projection_action_text" msgid="3634906766918186440">"Aloita nyt"</string>
@@ -707,8 +708,7 @@
     <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Pään seuranta"</string>
     <string name="volume_ringer_change" msgid="3574969197796055532">"Vaihda soittoäänen tilaa napauttamalla"</string>
     <string name="volume_ringer_mode" msgid="6867838048430807128">"Soittoäänen tila"</string>
-    <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
-    <skip />
+    <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, vaihda soittoäänen tilaa napauttamalla"</string>
     <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"mykistä"</string>
     <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"poista mykistys"</string>
     <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"värinä"</string>
@@ -873,9 +873,12 @@
     <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>
-    <string name="system_multitasking_lhs" msgid="8402954791206308783">"Käytä jaettua näyttöä niin, että nyt käytettävä sovellus on vasemmalla"</string>
-    <string name="system_multitasking_full_screen" msgid="336048080383640562">"Vaihda jaetusta näytöstä koko näyttöön"</string>
+    <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+    <skip />
+    <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+    <skip />
+    <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+    <skip />
     <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Vaihda sovellukseen oikealla tai alapuolella jaetussa näytössä"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Vaihda sovellukseen vasemmalla tai yläpuolella jaetussa näytössä"</string>
     <string name="system_multitasking_replace" msgid="7410071959803642125">"Jaetun näytön aikana: korvaa sovellus toisella"</string>
@@ -1438,8 +1441,7 @@
     <string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Toiminto- tai Meta-näppäinkuvake"</string>
     <string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Pluskuvake"</string>
     <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Muokkaa"</string>
-    <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
-    <skip />
+    <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Nollaa"</string>
     <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Valmis"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Laajennuskuvake"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"tai"</string>
@@ -1481,8 +1483,7 @@
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Paina näppäimistön toimintonäppäintä"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Hienoa!"</string>
     <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Olet oppinut Näytä kaikki sovellukset ‑eleen."</string>
-    <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
-    <skip />
+    <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Ohjeanimaatio, klikkaa keskeyttääksesi ja jatkaaksesi."</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Näppämistön taustavalo"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Taso %1$d/%2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Kodin ohjaus"</string>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index ea9ee0b..6d56ac1 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -529,9 +529,9 @@
     <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>
     <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgets"</string>
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
-    <skip />
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
+    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Pour ajouter le raccourci « Widgets », assurez-vous que « Afficher les widgets sur l\'écran de verrouillage » est activé dans les paramètres."</string>
+    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Paramètres"</string>
+    <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
     <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Changer d\'utilisateur"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menu déroulant"</string>
@@ -593,8 +593,7 @@
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Notifications"</string>
     <string name="notification_section_header_conversations" msgid="821834744538345661">"Conversations"</string>
     <string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Effacer toutes les notifications silencieuses"</string>
-    <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
-    <skip />
+    <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Ouvrir les paramètres des notifications"</string>
     <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Les notifications sont suspendues par le mode Ne pas déranger"</string>
     <string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Aucune notification}=1{Notifications suspendues par {mode}}=2{Notifications suspendues par {mode} et un autre mode}one{Notifications suspendues par {mode} et # autre mode}many{Notifications suspendues par {mode} et # d\'autres modes}other{Notifications suspendues par {mode} et # autres modes}}"</string>
     <string name="media_projection_action_text" msgid="3634906766918186440">"Commencer"</string>
@@ -709,8 +708,7 @@
     <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Suivi de la tête"</string>
     <string name="volume_ringer_change" msgid="3574969197796055532">"Touchez pour modifier le mode de sonnerie"</string>
     <string name="volume_ringer_mode" msgid="6867838048430807128">"mode de sonnerie"</string>
-    <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
-    <skip />
+    <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, toucher ici pour modifier le mode de la sonnerie"</string>
     <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"désactiver le son"</string>
     <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"réactiver le son"</string>
     <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibration"</string>
@@ -875,9 +873,12 @@
     <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>
+    <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+    <skip />
+    <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+    <skip />
+    <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+    <skip />
     <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>
@@ -1430,20 +1431,17 @@
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Raccourcis-clavier"</string>
     <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Personnaliser les raccourcis-clavier"</string>
     <string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Supprimer le raccourci?"</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Réinitialiser aux raccourcis par défaut?"</string>
     <string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Appuyez sur la touche pour attribuer un raccourci"</string>
     <string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Cela supprimera définitivement votre raccourci personnalisé."</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Cette action supprimera définitivement tous vos raccourcis personnalisés."</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Rechercher 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>
     <string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Icône de la touche Action ou Méta"</string>
     <string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Icône Plus"</string>
     <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Personnaliser"</string>
-    <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
-    <skip />
+    <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Réinitialiser"</string>
     <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Terminé"</string>
     <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>
@@ -1453,8 +1451,7 @@
     <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Paramètres du clavier"</string>
     <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Définir un raccourci"</string>
     <string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Supprimer"</string>
-    <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
-    <skip />
+    <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Oui, réinitialiser"</string>
     <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Annuler"</string>
     <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Appuyez sur la touche"</string>
     <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"La combinaison de touches est déjà utilisée. Essayez une autre touche."</string>
@@ -1486,8 +1483,7 @@
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Appuyez sur la touche d\'action de votre clavier"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Félicitations!"</string>
     <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Vous avez appris le geste pour afficher toutes les applis"</string>
-    <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
-    <skip />
+    <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Animation du tutoriel; cliquer ici pour mettre en pause et reprendre la lecture."</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Rétroéclairage du clavier"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Niveau %1$d de %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Domotique"</string>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index bda70d9..fd9854b 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -529,9 +529,9 @@
     <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>
     <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgets"</string>
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
-    <skip />
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
+    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Pour ajouter le raccourci \"Widgets\", assurez-vous que l\'option \"Afficher les widgets sur l\'écran de verrouillage\" est activée dans les paramètres."</string>
+    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Paramètres"</string>
+    <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
     <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Changer d\'utilisateur"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menu déroulant"</string>
@@ -593,8 +593,7 @@
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Notifications"</string>
     <string name="notification_section_header_conversations" msgid="821834744538345661">"Conversations"</string>
     <string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Effacer toutes les notifications silencieuses"</string>
-    <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
-    <skip />
+    <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Ouvrir les paramètres de notification"</string>
     <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Notifications suspendues par le mode Ne pas déranger"</string>
     <string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Aucune notification}=1{Notifications suspendues par le mode {mode}}=2{Notifications suspendues par le mode {mode} et un autre mode}one{Notifications suspendues par le mode {mode} et # autre mode}many{Notifications suspendues par le mode {mode} et # d\'autres modes}other{Notifications suspendues par le mode {mode} et # autres modes}}"</string>
     <string name="media_projection_action_text" msgid="3634906766918186440">"Commencer"</string>
@@ -709,8 +708,7 @@
     <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Suivi de la tête"</string>
     <string name="volume_ringer_change" msgid="3574969197796055532">"Appuyez pour changer le mode de la sonnerie"</string>
     <string name="volume_ringer_mode" msgid="6867838048430807128">"mode de sonnerie"</string>
-    <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
-    <skip />
+    <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, appuyez pour changer le mode de la sonnerie"</string>
     <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"couper le son"</string>
     <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"réactiver le son"</string>
     <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"activer le vibreur"</string>
@@ -875,9 +873,12 @@
     <string name="group_system_lock_screen" msgid="7391191300363416543">"Verrouiller l\'écran"</string>
     <string name="group_system_quick_memo" msgid="3764560265935722903">"Créer 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 partagé avec l\'appli actuelle sur la droite"</string>
-    <string name="system_multitasking_lhs" msgid="8402954791206308783">"Utiliser l\'écran partagé avec l\'appli actuelle sur la gauche"</string>
-    <string name="system_multitasking_full_screen" msgid="336048080383640562">"Passer de l\'écran partagé au plein écran"</string>
+    <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+    <skip />
+    <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+    <skip />
+    <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+    <skip />
     <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Passer à l\'appli à droite ou en dessous avec l\'écran partagé"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Passez à l\'appli à gauche ou au-dessus avec l\'écran partagé"</string>
     <string name="system_multitasking_replace" msgid="7410071959803642125">"En mode écran partagé : Remplacer une appli par une autre"</string>
@@ -1430,20 +1431,17 @@
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Raccourcis clavier"</string>
     <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Personnaliser les raccourcis clavier"</string>
     <string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Supprimer le raccourci ?"</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Rétablir les paramètres par défaut ?"</string>
     <string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Appuyez sur une touche pour attribuer un raccourci"</string>
     <string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Votre raccourci personnalisé sera définitivement supprimé."</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Tous vos raccourcis personnalisés seront définitivement supprimés."</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Rechercher 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>
     <string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Icône de touche d\'action ou de méta-touche"</string>
     <string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Icône Plus"</string>
     <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Personnaliser"</string>
-    <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
-    <skip />
+    <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Réinitialiser"</string>
     <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"OK"</string>
     <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>
@@ -1453,8 +1451,7 @@
     <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Paramètres du clavier"</string>
     <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Définir un raccourci"</string>
     <string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Supprimer"</string>
-    <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
-    <skip />
+    <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Oui, rétablir"</string>
     <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Annuler"</string>
     <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Appuyez sur la touche"</string>
     <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Combinaison de touches déjà utilisée. Essayez une autre touche."</string>
@@ -1486,8 +1483,7 @@
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Appuyez sur la touche d\'action de votre clavier"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Bravo !"</string>
     <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Vous avez appris le geste pour afficher toutes les applis"</string>
-    <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
-    <skip />
+    <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Animation du tutoriel, cliquez pour mettre en pause et reprendre la lecture."</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Rétroéclairage du clavier"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Niveau %1$d sur %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Contrôle de la maison"</string>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index 53c4417..96b858b6 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -529,9 +529,9 @@
     <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>
     <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgets"</string>
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
-    <skip />
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
+    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Para engadir o atallo Widgets, vai a Configuración e comproba que está activada a opción Mostrar widgets na pantalla de bloqueo."</string>
+    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Configuración"</string>
+    <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
     <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Cambiar usuario"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menú despregable"</string>
@@ -593,8 +593,7 @@
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Notificacións"</string>
     <string name="notification_section_header_conversations" msgid="821834744538345661">"Conversas"</string>
     <string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Borrar todas as notificacións silenciadas"</string>
-    <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
-    <skip />
+    <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Abrir a configuración de notificacións"</string>
     <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"O modo Non molestar puxo en pausa as notificacións"</string>
     <string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Non hai ningunha notificación}=1{Notificacións postas en pausa polo modo {mode}}=2{Notificacións postas en pausa polo modo {mode} e un máis}other{Notificacións postas en pausa polo modo {mode} e # máis}}"</string>
     <string name="media_projection_action_text" msgid="3634906766918186440">"Iniciar agora"</string>
@@ -709,8 +708,7 @@
     <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Seguimento da cabeza"</string>
     <string name="volume_ringer_change" msgid="3574969197796055532">"Toca para cambiar o modo de timbre"</string>
     <string name="volume_ringer_mode" msgid="6867838048430807128">"modo de timbre"</string>
-    <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
-    <skip />
+    <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, toca para cambiar o modo de timbre"</string>
     <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"silenciar"</string>
     <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"activar o son"</string>
     <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrar"</string>
@@ -875,9 +873,12 @@
     <string name="group_system_lock_screen" msgid="7391191300363416543">"Pantalla de bloqueo"</string>
     <string name="group_system_quick_memo" msgid="3764560265935722903">"Crear nota"</string>
     <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Multitarefa"</string>
-    <string name="system_multitasking_rhs" msgid="8714224917276297810">"Usar pantalla dividida coa aplicación actual na dereita"</string>
-    <string name="system_multitasking_lhs" msgid="8402954791206308783">"Usar pantalla dividida coa aplicación actual na esquerda"</string>
-    <string name="system_multitasking_full_screen" msgid="336048080383640562">"Cambiar de pantalla dividida a pantalla completa"</string>
+    <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+    <skip />
+    <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+    <skip />
+    <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+    <skip />
     <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Cambiar á aplicación da dereita ou de abaixo coa pantalla dividida"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Cambiar á aplicación da esquerda ou de arriba coa pantalla dividida"</string>
     <string name="system_multitasking_replace" msgid="7410071959803642125">"En modo de pantalla dividida: Substituír unha aplicación por outra"</string>
@@ -1430,20 +1431,17 @@
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Atallos de teclado"</string>
     <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Personalizar os atallos de teclado"</string>
     <string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Queres quitar o atallo?"</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Queres restablecer a opción predeterminada?"</string>
     <string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Preme a tecla para asignar o atallo"</string>
     <string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Eliminarase de forma permanente o teu atallo personalizado."</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Eliminaranse permanentemente todos os teus atallos personalizados."</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Busca atallos"</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>
     <string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Icona da tecla Meta ou de acción"</string>
     <string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Icona do signo máis"</string>
     <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Personalizar"</string>
-    <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
-    <skip />
+    <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Restablecer"</string>
     <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Feito"</string>
     <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>
@@ -1453,8 +1451,7 @@
     <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Configuración do teclado"</string>
     <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Definir atallo"</string>
     <string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Quitar"</string>
-    <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
-    <skip />
+    <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Si, restablecer"</string>
     <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Cancelar"</string>
     <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Preme unha tecla"</string>
     <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Xa se está usando esta combinación de teclas. Proba con outra."</string>
@@ -1486,8 +1483,7 @@
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Preme a tecla de acción do teclado"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Ben feito!"</string>
     <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Completaches o titorial do xesto de ver todas as aplicacións"</string>
-    <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
-    <skip />
+    <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Animación do titorial, fai clic para poñelo en pausa ou retomar a reprodución."</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Retroiluminación do teclado"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Nivel %1$d de %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Controis domóticos"</string>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index 8ff5717..fb494c1 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -531,6 +531,8 @@
     <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"વિજેટ"</string>
     <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"\"વિજેટ\"નો શૉર્ટકટ ઉમેરવા માટે, ખાતરી કરો કે સેટિંગમાં \"લૉક સ્ક્રીન પર વિજેટ બતાવો\" સુવિધા ચાલુ કરેલી છે."</string>
     <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"સેટિંગ"</string>
+    <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
+    <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"વપરાશકર્તા સ્વિચ કરો"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"પુલડાઉન મેનૂ"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"આ સત્રમાંની તમામ ઍપ અને ડેટા કાઢી નાખવામાં આવશે."</string>
@@ -591,8 +593,7 @@
     <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>
-    <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
-    <skip />
+    <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"નોટિફિકેશનના સેટિંગ ખોલો"</string>
     <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"ખલેલ પાડશો નહીં દ્વારા થોભાવેલ નોટિફિકેશન"</string>
     <string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{કોઈ નોટિફિકેશન નથી}=1{{mode} દ્વારા નોટિફિકેશન થોભાવવામાં આવ્યા}=2{{mode} અને અન્ય એક મોડ દ્વારા નોટિફિકેશન થોભાવવામાં આવ્યા}one{{mode} અને અન્ય # મોડ દ્વારા નોટિફિકેશન થોભાવવામાં આવ્યા}other{{mode} અને અન્ય # મોડ દ્વારા નોટિફિકેશન થોભાવવામાં આવ્યા}}"</string>
     <string name="media_projection_action_text" msgid="3634906766918186440">"હવે શરૂ કરો"</string>
@@ -707,8 +708,7 @@
     <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"હૅડ ટ્રૅકિંગ"</string>
     <string name="volume_ringer_change" msgid="3574969197796055532">"રિંગર મોડ બદલવા માટે ટૅપ કરો"</string>
     <string name="volume_ringer_mode" msgid="6867838048430807128">"રિંગર મોડ"</string>
-    <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
-    <skip />
+    <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, રિંગર મોડ બદલવા માટે ટૅપ કરો"</string>
     <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"મ્યૂટ કરો"</string>
     <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"અનમ્યૂટ કરો"</string>
     <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"વાઇબ્રેટ"</string>
@@ -873,9 +873,12 @@
     <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_full_screen" msgid="336048080383640562">"વિભાજિત સ્ક્રીનથી પૂર્ણ સ્ક્રીન પર સ્વિચ કરો"</string>
+    <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+    <skip />
+    <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+    <skip />
+    <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+    <skip />
     <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>
@@ -1438,8 +1441,7 @@
     <string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"ઍક્શન અથવા મેટા કીનું આઇકન"</string>
     <string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"પ્લસનું આઇકન"</string>
     <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"કસ્ટમાઇઝ કરો"</string>
-    <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
-    <skip />
+    <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"રીસેટ કરો"</string>
     <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"થઈ ગયું"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"\'મોટું કરો\'નું આઇકન"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"અથવા"</string>
@@ -1481,8 +1483,7 @@
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"તમારા કીબોર્ડ પરની ઍક્શન કી દબાવો"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"વાહ!"</string>
     <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"તમે \'બધી ઍપ જુઓ\' સંકેત પૂર્ણ કર્યો"</string>
-    <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
-    <skip />
+    <string name="tutorial_animation_content_description" msgid="2698816574982370184">"ટ્યૂટૉરિઅલ ઍનિમેશન થોભાવવાનું અને ચલાવવાનું ફરી શરૂ કરવા માટે ક્લિક કરો."</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"કીબોર્ડની બૅકલાઇટ"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$dમાંથી %1$d લેવલ"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"ઘરેલું સાધનોના નિયંત્રણો"</string>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index d2f6e5a..c84a3eb 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -450,7 +450,7 @@
     <string name="zen_modes_dialog_done" msgid="6654130880256438950">"हो गया"</string>
     <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"सेटिंग"</string>
     <string name="zen_mode_on" msgid="9085304934016242591">"चालू है"</string>
-    <string name="zen_mode_on_with_details" msgid="7416143430557895497">"<xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g> • पर"</string>
+    <string name="zen_mode_on_with_details" msgid="7416143430557895497">"चालू है • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string>
     <string name="zen_mode_off" msgid="1736604456618147306">"बंद है"</string>
     <string name="zen_mode_set_up" msgid="8231201163894922821">"सेट नहीं है"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"सेटिंग में जाकर मैनेज करें"</string>
@@ -529,10 +529,9 @@
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"किसी विजेट से कोई ऐप्लिकेशन खोलने के लिए, आपको अपनी पहचान की पुष्टि करनी होगी. ध्यान रखें कि आपके टैबलेट के लॉक होने पर भी, कोई व्यक्ति विजेट देख सकता है. ऐसा हो सकता है कि कुछ विजेट, लॉक स्क्रीन पर दिखाने के लिए न बने हों. इन्हें लॉक स्क्रीन पर जोड़ना असुरक्षित हो सकता है."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"ठीक है"</string>
     <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"विजेट"</string>
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
-    <skip />
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
-    <skip />
+    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"\"विजेट\" शॉर्टकट जोड़ने के लिए, पक्का करें कि सेटिंग में \"लॉक स्क्रीन पर विजेट दिखाएं\" चालू हो."</string>
+    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"सेटिंग"</string>
+    <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"स्क्रीन सेवर दिखाने का बटन"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"उपयोगकर्ता बदलें"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"पुलडाउन मेन्यू"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"इस सेशन के सभी ऐप्लिकेशन और डेटा को हटा दिया जाएगा."</string>
@@ -593,8 +592,7 @@
     <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>
-    <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
-    <skip />
+    <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"सूचना सेटिंग खोलें"</string>
     <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"\'परेशान न करें\' सुविधा के ज़रिए कुछ समय के लिए सूचनाएं दिखाना रोक दिया गया है"</string>
     <string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{कोई सूचना नहीं है}=1{{mode} की वजह से सूचना नहीं दिख रही है}=2{{mode} और एक अन्य मोड की वजह से सूचना नहीं दिख रही है}one{{mode} और # अन्य मोड की वजह से सूचना नहीं दिख रही है}other{{mode} और # अन्य मोड के की वजह से सूचनाएं नहीं दिख रही है}}"</string>
     <string name="media_projection_action_text" msgid="3634906766918186440">"अभी शुरू करें"</string>
@@ -709,8 +707,7 @@
     <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"हेड ट्रैकिंग चालू है"</string>
     <string name="volume_ringer_change" msgid="3574969197796055532">"रिंगर मोड बदलने के लिए टैप करें"</string>
     <string name="volume_ringer_mode" msgid="6867838048430807128">"रिंगर मोड"</string>
-    <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
-    <skip />
+    <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, रिंगर मोड बदलने के लिए टैप करें"</string>
     <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"म्यूट करें"</string>
     <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"अनम्यूट करें"</string>
     <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"वाइब्रेशन की सुविधा चालू करें"</string>
@@ -875,9 +872,12 @@
     <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_full_screen" msgid="336048080383640562">"स्प्लिट स्क्रीन से फ़ुल स्क्रीन मोड पर स्विच करने के लिए"</string>
+    <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+    <skip />
+    <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+    <skip />
+    <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+    <skip />
     <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>
@@ -1430,20 +1430,17 @@
     <string name="shortcut_helper_title" msgid="8567500639300970049">"कीबोर्ड शॉर्टकट"</string>
     <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"कीबोर्ड शॉर्टकट को पसंद के मुताबिक बनाएं"</string>
     <string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"क्या आपको शॉर्टकट हटाना है?"</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"क्या आपको फिर से डिफ़ॉल्ट सेटिंग चालू करनी है?"</string>
     <string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"शॉर्टकट असाइन करने के लिए बटन दबाएं"</string>
     <string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"ऐसा करने से, आपका कस्टम शॉर्टकट हमेशा के लिए मिट जाएगा."</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"ऐसा करने पर, आपके सभी कस्टम शॉर्टकट हमेशा के लिए मिट जाएंगे."</string>
     <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>
     <string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"ऐक्शन या मेटा बटन का आइकॉन"</string>
     <string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"प्लस का आइकॉन"</string>
     <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"पसंद के मुताबिक बनाएं"</string>
-    <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
-    <skip />
+    <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"रीसेट करें"</string>
     <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"हो गया"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"बड़ा करने का आइकॉन"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"या"</string>
@@ -1453,8 +1450,7 @@
     <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"कीबोर्ड सेटिंग"</string>
     <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"शॉर्टकट सेट करें"</string>
     <string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"हटाएं"</string>
-    <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
-    <skip />
+    <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"हां, रीसेट करें"</string>
     <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"रद्द करें"</string>
     <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"बटन दबाएं"</string>
     <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"बटन का यह कॉम्बिनेशन पहले से इस्तेमाल किया जा रहा है. कोई दूसरा कॉम्बिनेशन आज़माएं."</string>
@@ -1486,8 +1482,7 @@
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"अपने कीबोर्ड पर ऐक्शन बटन दबाएं"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"बहुत खूब!"</string>
     <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"अब आपको हाथ के जेस्चर का इस्तेमाल करके, सभी ऐप्लिकेशन देखने का तरीका पता चल गया है"</string>
-    <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
-    <skip />
+    <string name="tutorial_animation_content_description" msgid="2698816574982370184">"ट्यूटोरियल ऐनिमेशन को रोकने और इन्हें फिर से चलाने के लिए क्लिक करें."</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"कीबोर्ड की बैकलाइट"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d में से %1$d लेवल"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"होम कंट्रोल"</string>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index e566e4e..6eabb6a 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -531,6 +531,7 @@
     <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgeti"</string>
     <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Da biste dodali prečac Widgeti, provjerite je li u postavkama omogućena opcija Prikaži widgete na zaključanom zaslonu."</string>
     <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Postavke"</string>
+    <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Prikaži gumb čuvara zaslona"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Promjena korisnika"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"padajući izbornik"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Izbrisat će se sve aplikacije i podaci u ovoj sesiji."</string>
@@ -591,8 +592,7 @@
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Obavijesti"</string>
     <string name="notification_section_header_conversations" msgid="821834744538345661">"Razgovori"</string>
     <string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Izbriši sve bešumne obavijesti"</string>
-    <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
-    <skip />
+    <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Otvori postavke obavijesti"</string>
     <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Značajka Ne uznemiravaj pauzirala je Obavijesti"</string>
     <string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Nema obavijesti}=1{Obavijesti je pauzirao način {mode}}=2{Obavijesti su pauzirali način {mode} i još jedan način}one{Obavijesti su pauzirali način {mode} i još # način rada}few{Obavijesti su pauzirali način {mode} i još # načina rada}other{Obavijesti su pauzirali način {mode} i još # načina rada}}"</string>
     <string name="media_projection_action_text" msgid="3634906766918186440">"Pokreni"</string>
@@ -707,8 +707,7 @@
     <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Praćenje glave"</string>
     <string name="volume_ringer_change" msgid="3574969197796055532">"Dodirnite da biste promijenili način softvera zvona"</string>
     <string name="volume_ringer_mode" msgid="6867838048430807128">"način softvera zvona"</string>
-    <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
-    <skip />
+    <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, dodirnite da biste promijenili način softvera zvona"</string>
     <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"isključivanje zvuka"</string>
     <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"uključivanje zvuka"</string>
     <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibriranje"</string>
@@ -873,9 +872,12 @@
     <string name="group_system_lock_screen" msgid="7391191300363416543">"Zaključavanje zaslona"</string>
     <string name="group_system_quick_memo" msgid="3764560265935722903">"Pisanje bilješke"</string>
     <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Obavljanje više zadataka"</string>
-    <string name="system_multitasking_rhs" msgid="8714224917276297810">"Koristite podijeljeni zaslon s trenutačnom aplikacijom s desne strane"</string>
-    <string name="system_multitasking_lhs" msgid="8402954791206308783">"Koristite podijeljeni zaslon s trenutačnom aplikacijom s lijeve strane"</string>
-    <string name="system_multitasking_full_screen" msgid="336048080383640562">"Prelazak s podijeljenog zaslona na cijeli zaslon"</string>
+    <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+    <skip />
+    <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+    <skip />
+    <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+    <skip />
     <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Prelazak na aplikaciju zdesna ili ispod uz podijeljeni zaslon"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Prelazak na aplikaciju slijeva ili iznad uz podijeljeni zaslon"</string>
     <string name="system_multitasking_replace" msgid="7410071959803642125">"Tijekom podijeljenog zaslona: zamijeni aplikaciju drugom"</string>
@@ -1438,8 +1440,7 @@
     <string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Ikona tipke za radnju odnosno meta tipka"</string>
     <string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Ikona plusa"</string>
     <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Prilagodi"</string>
-    <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
-    <skip />
+    <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Poništi"</string>
     <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Gotovo"</string>
     <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>
@@ -1481,8 +1482,7 @@
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Pritisnite tipku za radnju na tipkovnici"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Izvrsno!"</string>
     <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Napravili ste pokret za prikaz svih aplikacija"</string>
-    <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
-    <skip />
+    <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Animacija u vodiču, kliknite za pauziranje i nastavak reprodukcije."</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Pozadinsko osvjetljenje tipkovnice"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Razina %1$d od %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Upravljanje uređajima"</string>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index 0cbf0aa..2ee7b24 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -529,9 +529,9 @@
     <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>
     <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Modulok"</string>
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
-    <skip />
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
+    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"A „Modulok” gyorsparancs hozzáadásához gondoskodjon arról, hogy a „Modulok megjelenítése a lezárási képernyőn” beállítás legyen engedélyezve a beállításokban."</string>
+    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Beállítások"</string>
+    <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
     <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Felhasználóváltás"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"lehúzható menü"</string>
@@ -593,8 +593,7 @@
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Értesítések"</string>
     <string name="notification_section_header_conversations" msgid="821834744538345661">"Beszélgetések"</string>
     <string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Az összes néma értesítés törlése"</string>
-    <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
-    <skip />
+    <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Értesítési beállítások megnyitása"</string>
     <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Ne zavarjanak funkcióval szüneteltetett értesítések"</string>
     <string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Nincs értesítés}=1{A(z) {mode} szüneteltette az értesítéseket}=2{A(z) {mode} és egy másik mód szüneteltette az értesítéseket}other{A(z) {mode} és # másik mód szüneteltette az értesítéseket}}"</string>
     <string name="media_projection_action_text" msgid="3634906766918186440">"Indítás most"</string>
@@ -709,8 +708,7 @@
     <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Fejkövetés"</string>
     <string name="volume_ringer_change" msgid="3574969197796055532">"Koppintson a csengés módjának módosításához"</string>
     <string name="volume_ringer_mode" msgid="6867838048430807128">"csengés módja"</string>
-    <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
-    <skip />
+    <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>. Koppintson a csörgés módjának módosításához."</string>
     <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"némítás"</string>
     <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"némítás feloldása"</string>
     <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"rezgés"</string>
@@ -875,9 +873,12 @@
     <string name="group_system_lock_screen" msgid="7391191300363416543">"Lezárási képernyő"</string>
     <string name="group_system_quick_memo" msgid="3764560265935722903">"Jegyzetelés"</string>
     <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Multitasking"</string>
-    <string name="system_multitasking_rhs" msgid="8714224917276297810">"Osztott képernyő használata, a jelenlegi alkalmazás legyen jobb oldalt"</string>
-    <string name="system_multitasking_lhs" msgid="8402954791206308783">"Osztott képernyő használata, a jelenlegi alkalmazás legyen bal oldalt"</string>
-    <string name="system_multitasking_full_screen" msgid="336048080383640562">"Váltás osztott képernyőről teljes képernyőre"</string>
+    <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+    <skip />
+    <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+    <skip />
+    <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+    <skip />
     <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Váltás a jobb oldalt, illetve lent lévő appra osztott képernyő esetén"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Váltás a bal oldalt, illetve fent lévő appra osztott képernyő esetén"</string>
     <string name="system_multitasking_replace" msgid="7410071959803642125">"Osztott képernyőn: az egyik alkalmazás lecserélése egy másikra"</string>
@@ -1430,20 +1431,17 @@
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Billentyűparancsok"</string>
     <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"A billentyűparancsok személyre szabása"</string>
     <string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Eltávolítja a billentyűparancsot?"</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Visszaállítja az alapértelmezett beállításokat?"</string>
     <string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Nyomja meg a billentyűt a billentyűparancs hozzárendeléséhez"</string>
     <string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Ezzel véglegesen törli az egyéni billentyűparancsot."</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Ezzel véglegesen törli az összes egyéni billentyűparancsot."</string>
     <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>
     <string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Művelet vagy Meta billentyű ikonja"</string>
     <string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Pluszikon"</string>
     <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Személyre szabás"</string>
-    <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
-    <skip />
+    <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Visszaállítás"</string>
     <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Kész"</string>
     <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>
@@ -1453,8 +1451,7 @@
     <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Billentyűzetbeállítások"</string>
     <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Billentyűparancs beállítása"</string>
     <string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Eltávolítás"</string>
-    <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
-    <skip />
+    <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Igen, visszaállítom"</string>
     <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Mégse"</string>
     <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Nyomja le a billentyűt"</string>
     <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"A billentyűkombináció már használatban van. Próbálkozzon másik billentyűvel."</string>
@@ -1486,8 +1483,7 @@
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Nyomja meg a műveletbillentyűt az érintőpadon."</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Szép munka!"</string>
     <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Teljesítette az összes alkalmazás megtekintésének kézmozdulatát."</string>
-    <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
-    <skip />
+    <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Útmutató animáció. Kattintson a szüneteltetéshez és a lejátszás folytatásához."</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"A billentyűzet háttérvilágítása"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Fényerő: %2$d/%1$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Otthon vezérlése"</string>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index 867e0da..d43beb8 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -529,9 +529,9 @@
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Վիջեթի միջոցով հավելված բացելու համար դուք պետք է հաստատեք ձեր ինքնությունը։ Նաև նկատի ունեցեք, որ ցանկացած ոք կարող է դիտել վիջեթները, նույնիսկ երբ ձեր պլանշետը կողպված է։ Որոշ վիջեթներ կարող են նախատեսված չլինել ձեր կողպէկրանի համար, և այստեղ դրանց ավելացնելը կարող է վտանգավոր լինել։"</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Եղավ"</string>
     <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Վիջեթներ"</string>
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
-    <skip />
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
+    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"«Վիջեթներ» դյուրանցումն ավելացնելու համար համոզվեք, որ «Ցույց տալ վիջեթները կողպէկրանին» պարամետրը միացված է կարգավորումներում։"</string>
+    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Կարգավորումներ"</string>
+    <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
     <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Անջատել օգտվողին"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"իջնող ընտրացանկ"</string>
@@ -593,8 +593,7 @@
     <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>
-    <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
-    <skip />
+    <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Բացել ծանուցումների կարգավորումները"</string>
     <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Ծանուցումները չեն ցուցադրվի «Չանհանգստացնել» ռեժիմում"</string>
     <string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Ծանուցումներ չկան}=1{Ծանուցումները դադարեցվել են «{mode}» ռեժիմի կողմից}=2{Ծանուցումները դադարեցվել են «{mode}» ու ևս մի ռեժիմի կողմից}one{Ծանուցումները դադարեցվել են «{mode}» ու ևս # ռեժիմի կողմից}other{Ծանուցումները դադարեցվել են «{mode}» ու ևս # ռեժիմի կողմից}}"</string>
     <string name="media_projection_action_text" msgid="3634906766918186440">"Սկսել հիմա"</string>
@@ -709,8 +708,7 @@
     <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Գլխի շարժումների հետագծում"</string>
     <string name="volume_ringer_change" msgid="3574969197796055532">"Հպեք՝ զանգակի ռեժիմը փոխելու համար"</string>
     <string name="volume_ringer_mode" msgid="6867838048430807128">"զանգակի ռեժիմ"</string>
-    <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
-    <skip />
+    <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>․ հպեք՝ զանգակի ռեժիմը փոխելու համար"</string>
     <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"անջատել ձայնը"</string>
     <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"միացնել ձայնը"</string>
     <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"միացնել թրթռոցը"</string>
@@ -875,9 +873,12 @@
     <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_full_screen" msgid="336048080383640562">"Տրոհված էկրանից անցնել լիաէկրան ռեժիմ"</string>
+    <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+    <skip />
+    <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+    <skip />
+    <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+    <skip />
     <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>
@@ -1430,20 +1431,17 @@
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Ստեղնային դյուրանցումներ"</string>
     <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Կարգավորեք ստեղնային դյուրանցումներ"</string>
     <string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Հեռացնե՞լ դյուրանցումը"</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Վերականգնե՞լ կանխադրվածները"</string>
     <string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Սեղմեք որևէ ստեղն՝ դյուրանցում նշանակելու համար"</string>
     <string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Ձեր հատուկ դյուրանցումն ընդմիշտ կջնջվի։"</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Բոլոր հատուկ դյուրանցումներն ընդմիշտ կջնջվեն։"</string>
     <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>
     <string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Գործողության կամ Meta ստեղնի պատկերակ"</string>
     <string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Պլյուս պատկերակ"</string>
     <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Կարգավորել"</string>
-    <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
-    <skip />
+    <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Զրոյացնել"</string>
     <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Պատրաստ է"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ծավալել պատկերակը"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"կամ"</string>
@@ -1453,8 +1451,7 @@
     <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Ստեղնաշարի կարգավորումներ"</string>
     <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Ստեղծել դյուրանցում"</string>
     <string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Հեռացնել"</string>
-    <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
-    <skip />
+    <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Այո, վերականգնել"</string>
     <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Չեղարկել"</string>
     <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Սեղմեք որևէ ստեղն"</string>
     <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Ստեղների համակցությունն արդեն օգտագործվում է։ Ընտրեք այլ ստեղն։"</string>
@@ -1486,8 +1483,7 @@
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Սեղմեք գործողության ստեղնը ստեղնաշարի վրա"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Հիանալի՛ է"</string>
     <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Դուք սովորեցիք բոլոր հավելվածները դիտելու ժեստը"</string>
-    <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
-    <skip />
+    <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Ուղեցույցի անիմացիա․ սեղմեք՝ նվագարկումը դադարեցնելու/վերսկսելու համար։"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Հետին լուսավորությամբ ստեղնաշար"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"%1$d՝ %2$d-ից"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Տան կառավարման տարրեր"</string>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index 06ecd11..095b35b 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -529,9 +529,9 @@
     <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>
     <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widget"</string>
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
-    <skip />
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
+    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Untuk menambahkan pintasan \"Widget\", pastikan \"Tampilkan widget di layar kunci\" diaktifkan di setelan."</string>
+    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Setelan"</string>
+    <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
     <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Beralih pengguna"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menu pulldown"</string>
@@ -593,8 +593,7 @@
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Notifikasi"</string>
     <string name="notification_section_header_conversations" msgid="821834744538345661">"Percakapan"</string>
     <string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Hapus semua notifikasi senyap"</string>
-    <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
-    <skip />
+    <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Buka setelan notifikasi"</string>
     <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Notifikasi dijeda oleh mode Jangan Ganggu"</string>
     <string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Tidak ada notifikasi}=1{Notifikasi dijeda oleh {mode}}=2{Notifikasi dijeda oleh {mode} dan satu mode lainnya}other{Notifikasi dijeda oleh {mode} dan # mode lainnya}}"</string>
     <string name="media_projection_action_text" msgid="3634906766918186440">"Mulai sekarang"</string>
@@ -709,8 +708,7 @@
     <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Pelacakan Gerak Kepala"</string>
     <string name="volume_ringer_change" msgid="3574969197796055532">"Ketuk untuk mengubah mode pendering"</string>
     <string name="volume_ringer_mode" msgid="6867838048430807128">"mode pendering"</string>
-    <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
-    <skip />
+    <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, ketuk untuk mengubah mode pendering"</string>
     <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"Tanpa suara"</string>
     <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"aktifkan"</string>
     <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"getar"</string>
@@ -875,9 +873,12 @@
     <string name="group_system_lock_screen" msgid="7391191300363416543">"Kunci layar"</string>
     <string name="group_system_quick_memo" msgid="3764560265935722903">"Buat catatan"</string>
     <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Multitasking"</string>
-    <string name="system_multitasking_rhs" msgid="8714224917276297810">"Gunakan layar terpisah dengan aplikasi saat ini di sebelah kanan"</string>
-    <string name="system_multitasking_lhs" msgid="8402954791206308783">"Gunakan layar terpisah dengan aplikasi saat ini di sebelah kiri"</string>
-    <string name="system_multitasking_full_screen" msgid="336048080383640562">"Beralih dari layar terpisah ke layar penuh"</string>
+    <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+    <skip />
+    <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+    <skip />
+    <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+    <skip />
     <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Beralih ke aplikasi di bagian kanan atau bawah saat menggunakan layar terpisah"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Beralih ke aplikasi di bagian kiri atau atas saat menggunakan layar terpisah"</string>
     <string name="system_multitasking_replace" msgid="7410071959803642125">"Dalam layar terpisah: ganti salah satu aplikasi dengan yang lain"</string>
@@ -1430,20 +1431,17 @@
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Pintasan keyboard"</string>
     <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Menyesuaikan pintasan keyboard"</string>
     <string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Hapus pintasan?"</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Reset kembali ke pintasan default?"</string>
     <string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Tekan tombol untuk menetapkan pintasan"</string>
     <string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Tindakan ini akan menghapus pintasan kustom Anda secara permanen."</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Tindakan ini akan menghapus semua pintasan kustom Anda secara permanen."</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Telusuri pintasan"</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>
     <string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Ikon tombol Tindakan atau Meta"</string>
     <string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Ikon plus"</string>
     <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Sesuaikan"</string>
-    <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
-    <skip />
+    <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Reset"</string>
     <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Selesai"</string>
     <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>
@@ -1453,8 +1451,7 @@
     <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Setelan Keyboard"</string>
     <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Setel pintasan"</string>
     <string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Hapus"</string>
-    <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
-    <skip />
+    <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Ya, reset"</string>
     <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Batal"</string>
     <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Tekan tombol"</string>
     <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Kombinasi tombol sudah digunakan. Coba tombol lain."</string>
@@ -1486,8 +1483,7 @@
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Tekan tombol tindakan di keyboard"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Oke!"</string>
     <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Anda telah menyelesaikan gestur untuk melihat semua aplikasi"</string>
-    <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
-    <skip />
+    <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Animasi tutorial, klik untuk menjeda dan melanjutkan pemutaran."</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Lampu latar keyboard"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Tingkat %1$d dari %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Kontrol Rumah"</string>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index 4f6ff47..8a885f0 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -531,6 +531,7 @@
     <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Græjur"</string>
     <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Gakktu úr skugga um að kveikt sé á „Sýna græjur á lásskjá“ til að geta bætt flýtileiðinni „Græjur“ við."</string>
     <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Stillingar"</string>
+    <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Hnappurinn „Sýna skjávara“"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Skipta um notanda"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"Fellivalmynd"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Öllum forritum og gögnum í þessari lotu verður eytt."</string>
@@ -591,8 +592,7 @@
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Tilkynningar"</string>
     <string name="notification_section_header_conversations" msgid="821834744538345661">"Samtöl"</string>
     <string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Hreinsa allar þöglar tilkynningar"</string>
-    <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
-    <skip />
+    <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Opna tilkynningastillingar"</string>
     <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Hlé gert á tilkynningum þar sem stillt er á „Ónáðið ekki“"</string>
     <string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Engar tilkynningar}=1{Hlé gert á tilkynningum þar sem stillt er á {mode}}=2{Hlé gert á tilkynningum þar sem stillt er á {mode} og eina aðra stillingu}one{Hlé gert á tilkynningum þar sem stillt er á {mode} og # aðra stillingu}other{Hlé gert á tilkynningum þar sem stillt er á {mode} og # aðrar stillingar}}"</string>
     <string name="media_projection_action_text" msgid="3634906766918186440">"Byrja núna"</string>
@@ -707,8 +707,7 @@
     <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Rakning höfuðhreyfinga"</string>
     <string name="volume_ringer_change" msgid="3574969197796055532">"Ýta til að skipta um hringjarastillingu"</string>
     <string name="volume_ringer_mode" msgid="6867838048430807128">"hringistilling"</string>
-    <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
-    <skip />
+    <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, ýttu til að skipta um hringjarastillingu"</string>
     <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"þagga"</string>
     <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"hætta að þagga"</string>
     <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"titringur"</string>
@@ -873,9 +872,12 @@
     <string name="group_system_lock_screen" msgid="7391191300363416543">"Lásskjár"</string>
     <string name="group_system_quick_memo" msgid="3764560265935722903">"Skrifa glósu"</string>
     <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Fjölvinnsla"</string>
-    <string name="system_multitasking_rhs" msgid="8714224917276297810">"Notaðu skjáskiptingu með núverandi forriti til hægri"</string>
-    <string name="system_multitasking_lhs" msgid="8402954791206308783">"Notaðu skjáskiptingu með núverandi forriti til vinstri"</string>
-    <string name="system_multitasking_full_screen" msgid="336048080383640562">"Skipta úr skjáskiptingu yfir á allan skjáinn"</string>
+    <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+    <skip />
+    <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+    <skip />
+    <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+    <skip />
     <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Skiptu í forrit til hægri eða fyrir neðan þegar skjáskipting er notuð"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Skiptu í forrit til vinstri eða fyrir ofan þegar skjáskipting er notuð"</string>
     <string name="system_multitasking_replace" msgid="7410071959803642125">"Í skjáskiptingu: Skipta forriti út fyrir annað forrit"</string>
@@ -1438,8 +1440,7 @@
     <string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Tákn lýsilykils (aðgerðarlykils)"</string>
     <string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Plústákn"</string>
     <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Sérsníða"</string>
-    <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
-    <skip />
+    <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Endurstilla"</string>
     <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Lokið"</string>
     <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>
@@ -1481,8 +1482,7 @@
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Ýttu á aðgerðalykilinn á lyklaborðinu"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Vel gert!"</string>
     <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Þú framkvæmdir bendinguna „Sjá öll forrit“"</string>
-    <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
-    <skip />
+    <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Leiðsagnarhreyfimynd, smelltu til að gera hlé og halda áfram að spila."</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Baklýsing lyklaborðs"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Stig %1$d af %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Heimastýringar"</string>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 76e3360..790a80d 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -531,6 +531,7 @@
     <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widget"</string>
     <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Per aggiungere la scorciatoia \"Widget\", assicurati che l\'opzione \"Mostra widget sulla schermata di blocco\" sia abilitata nelle impostazioni."</string>
     <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Impostazioni"</string>
+    <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Pulsante Mostra salvaschermo"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Cambio utente"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menu a discesa"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Tutte le app e i dati di questa sessione verranno eliminati."</string>
@@ -591,8 +592,7 @@
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Notifiche"</string>
     <string name="notification_section_header_conversations" msgid="821834744538345661">"Conversazioni"</string>
     <string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Cancella tutte le notifiche silenziose"</string>
-    <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
-    <skip />
+    <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Apri impostazioni di notifica"</string>
     <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Notifiche messe in pausa in base alla modalità Non disturbare"</string>
     <string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Nessuna notifica}=1{Notifica messa in pausa da {mode}}=2{Notifiche messe in pausa da {mode} e un\'altra modalità}many{Notifiche messe in pausa da {mode} e # di modalità}other{Notifiche messe in pausa da {mode} e altre # modalità}}"</string>
     <string name="media_projection_action_text" msgid="3634906766918186440">"Avvia adesso"</string>
@@ -707,8 +707,7 @@
     <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Rilev. movim. testa"</string>
     <string name="volume_ringer_change" msgid="3574969197796055532">"Tocca per cambiare la modalità della suoneria"</string>
     <string name="volume_ringer_mode" msgid="6867838048430807128">"modalità suoneria"</string>
-    <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
-    <skip />
+    <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, tocca per cambiare la modalità della suoneria"</string>
     <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"silenzia"</string>
     <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"riattiva l\'audio"</string>
     <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrazione"</string>
@@ -873,9 +872,9 @@
     <string name="group_system_lock_screen" msgid="7391191300363416543">"Blocca lo schermo"</string>
     <string name="group_system_quick_memo" msgid="3764560265935722903">"Scrivi una nota"</string>
     <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Multitasking"</string>
-    <string name="system_multitasking_rhs" msgid="8714224917276297810">"Utilizza schermo diviso con l\'app corrente a destra"</string>
-    <string name="system_multitasking_lhs" msgid="8402954791206308783">"Utilizza schermo diviso con l\'app corrente a sinistra"</string>
-    <string name="system_multitasking_full_screen" msgid="336048080383640562">"Passa da schermo diviso a schermo intero"</string>
+    <string name="system_multitasking_rhs" msgid="8779289852395243004">"Utilizza lo schermo diviso con l\'app a destra"</string>
+    <string name="system_multitasking_lhs" msgid="7348595296208696452">"Utilizza lo schermo diviso con l\'app a sinistra"</string>
+    <string name="system_multitasking_full_screen" msgid="4940465971687159429">"Passa alla modalità a schermo intero"</string>
     <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Passa all\'app a destra o sotto mentre usi lo schermo diviso"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Passa all\'app a sinistra o sopra mentre usi lo schermo diviso"</string>
     <string name="system_multitasking_replace" msgid="7410071959803642125">"Con lo schermo diviso: sostituisci un\'app con un\'altra"</string>
@@ -1438,8 +1437,7 @@
     <string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Icona tasto Azione o Meta"</string>
     <string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Icona Più"</string>
     <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Personalizza"</string>
-    <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
-    <skip />
+    <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Reimposta"</string>
     <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Fine"</string>
     <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>
@@ -1481,8 +1479,7 @@
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Premi il tasto azione sulla tastiera"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Ben fatto!"</string>
     <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Hai completato il gesto Visualizza tutte le app."</string>
-    <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
-    <skip />
+    <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Animazione del tutorial: fai clic per mettere in pausa e riprendere la riproduzione."</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Retroilluminazione della tastiera"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Livello %1$d di %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Controlli della casa"</string>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 3257e14..99939a8 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -529,9 +529,9 @@
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"כדי לפתוח אפליקציה באמצעות ווידג\'ט, עליך לאמת את זהותך. בנוסף, כדאי לזכור שכל אחד יכול לראות את הווידג\'טים גם כשהטאבלט שלך נעול. יכול להיות שחלק מהווידג\'טים לא נועדו למסך הנעילה ושלא בטוח להוסיף אותם לכאן."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"הבנתי"</string>
     <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"ווידג\'טים"</string>
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
-    <skip />
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
+    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"כדי להוסיף את קיצור הדרך \"ווידג\'טים\", צריך לוודא שהאפשרות \"ווידג\'טים במסך הנעילה\" מופעלת בהגדרות."</string>
+    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"הגדרות"</string>
+    <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
     <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"החלפת משתמש"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"תפריט במשיכה למטה"</string>
@@ -593,8 +593,7 @@
     <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>
-    <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
-    <skip />
+    <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"פתיחה של הגדרת ההתראות"</string>
     <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"התראות הושהו על ידי מצב \'נא לא להפריע\'"</string>
     <string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{אין התראות}=1{ההתראות הושהו על ידי {mode}}=2{ההתראות הושהו על ידי {mode} ועל ידי מצב אחד נוסף}one{ההתראות הושהו על ידי {mode} ועל ידי # מצבים נוספים}other{ההתראות הושהו על ידי {mode} ועל ידי # מצבים נוספים}}"</string>
     <string name="media_projection_action_text" msgid="3634906766918186440">"כן, אפשר להתחיל"</string>
@@ -709,8 +708,7 @@
     <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"מעקב ראש"</string>
     <string name="volume_ringer_change" msgid="3574969197796055532">"יש להקיש כדי לשנות את מצב תוכנת הצלצול"</string>
     <string name="volume_ringer_mode" msgid="6867838048430807128">"מצב תוכנת הצלצול"</string>
-    <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
-    <skip />
+    <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, צריך להקיש כדי לשנות את מצב תוכנת הצלצול"</string>
     <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"השתקה"</string>
     <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ביטול ההשתקה"</string>
     <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"רטט"</string>
@@ -875,9 +873,12 @@
     <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_full_screen" msgid="336048080383640562">"החלפה ממסך מפוצל למסך מלא"</string>
+    <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+    <skip />
+    <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+    <skip />
+    <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+    <skip />
     <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>
@@ -1430,20 +1431,17 @@
     <string name="shortcut_helper_title" msgid="8567500639300970049">"מקשי קיצור"</string>
     <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"התאמה אישית של מקשי הקיצור"</string>
     <string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"להסיר את קיצור הדרך?"</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"לאפס לברירת המחדל?"</string>
     <string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"צריך להקיש על מקש כדי להקצות מקש קיצור"</string>
     <string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"קיצור הדרך יימחק באופן סופי."</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"הפעולה הזו תמחק באופן סופי את כל קיצורי הדרך המותאמים אישית."</string>
     <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>
     <string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"סמל מקש הפעולה (\"מטא\")"</string>
     <string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"סמל הפלוס"</string>
     <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"התאמה אישית"</string>
-    <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
-    <skip />
+    <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"איפוס"</string>
     <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"סיום"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"סמל ההרחבה"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"או"</string>
@@ -1453,8 +1451,7 @@
     <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"הגדרות המקלדת"</string>
     <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"הגדרה של מקש קיצור"</string>
     <string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"הסרה"</string>
-    <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
-    <skip />
+    <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"כן, לאפס"</string>
     <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"ביטול"</string>
     <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"יש ללחוץ על מקש"</string>
     <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"שילוב המקשים הזה כבר בשימוש. אפשר לנסות מקש אחר."</string>
@@ -1486,8 +1483,7 @@
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"צריך להקיש על מקש הפעולה במקלדת"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"כל הכבוד!"</string>
     <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"סיימת לתרגל את התנועה להצגת כל האפליקציות"</string>
-    <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
-    <skip />
+    <string name="tutorial_animation_content_description" msgid="2698816574982370184">"אנימציה של הדרכה, אפשר ללחוץ כדי להשהות ולהמשיך את ההפעלה."</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"התאורה האחורית במקלדת"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"‏רמה %1$d מתוך %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"שליטה במכשירים"</string>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index 13dd954..5fd029b2 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -531,6 +531,7 @@
     <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"ウィジェット"</string>
     <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"[ウィジェット] ショートカットを追加するには、設定で [ロック画面でのウィジェットの表示] が有効になっていることを確認してください。"</string>
     <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"設定"</string>
+    <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"スクリーンセーバー表示ボタン"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ユーザーを切り替える"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"プルダウン メニュー"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"このセッションでのアプリとデータはすべて削除されます。"</string>
@@ -591,8 +592,7 @@
     <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>
-    <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
-    <skip />
+    <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"通知設定を開く"</string>
     <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"サイレント モードにより通知は一時停止中です"</string>
     <string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{通知なし}=1{{mode} により通知は一時停止中です}=2{{mode} と他 1 個のモードにより通知は一時停止中です}other{{mode} と他 # 個のモードにより通知は一時停止中です}}"</string>
     <string name="media_projection_action_text" msgid="3634906766918186440">"今すぐ開始"</string>
@@ -707,8 +707,7 @@
     <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"ヘ⁠ッ⁠ド ト⁠ラ⁠ッ⁠キ⁠ン⁠グ"</string>
     <string name="volume_ringer_change" msgid="3574969197796055532">"タップすると、着信音のモードを変更できます"</string>
     <string name="volume_ringer_mode" msgid="6867838048430807128">"着信音のモード"</string>
-    <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
-    <skip />
+    <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>、タップすると、着信音のモードを変更できます"</string>
     <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"ミュート"</string>
     <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ミュートを解除"</string>
     <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"バイブレーション"</string>
@@ -873,9 +872,12 @@
     <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_full_screen" msgid="336048080383640562">"分割画面から全画面に切り替える"</string>
+    <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+    <skip />
+    <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+    <skip />
+    <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+    <skip />
     <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>
@@ -1480,8 +1482,7 @@
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"キーボードのアクションキーを押します"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"完了です!"</string>
     <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"「すべてのアプリを表示する」ジェスチャーを学習しました"</string>
-    <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
-    <skip />
+    <string name="tutorial_animation_content_description" msgid="2698816574982370184">"チュートリアルのアニメーションです。クリックすると一時停止、もう一度クリックすると再開します。"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"キーボード バックライト"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"レベル %1$d/%2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"ホーム コントロール"</string>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index 9390bd2..55d38c9 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -531,6 +531,7 @@
     <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"ვიჯეტები"</string>
     <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"„ვიჯეტების“ მალსახმობის დასამატებლად დარწმუნდით, რომ პარამეტრებში ჩართულია „დაბლოკილ ეკრანზე ვიჯეტების ჩვენება“."</string>
     <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"პარამეტრები"</string>
+    <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"ეკრანმზოგის ღილაკის ჩვენება"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"მომხმარებლის გადართვა"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ჩამოშლადი მენიუ"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ამ სესიის ყველა აპი და მონაცემი წაიშლება."</string>
@@ -591,8 +592,7 @@
     <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>
-    <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
-    <skip />
+    <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"შეტყობინების პარამეტრების გახსნა"</string>
     <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"შეტყობინებები დაპაუზდა „არ შემაწუხოთ“ რეჟიმის მეშვეობით"</string>
     <string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{შეტყობინებები არ არის}=1{შეტყობინებები შეჩერებულია {mode}-ის გამო}=2{შეტყობინებები შეჩერებულია {mode}-ის და ერთი სხვა რეჟიმის გამო}other{შეტყობინებები შეჩერებულია {mode}-ის და # სხვა რეჟიმის გამო}}"</string>
     <string name="media_projection_action_text" msgid="3634906766918186440">"დაწყება ახლავე"</string>
@@ -707,8 +707,7 @@
     <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"ხმის მიდევნებით"</string>
     <string name="volume_ringer_change" msgid="3574969197796055532">"შეეხეთ მრეკავის რეჟიმის შესაცვლელად"</string>
     <string name="volume_ringer_mode" msgid="6867838048430807128">"მრეკავის რეჟიმი"</string>
-    <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
-    <skip />
+    <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, შეეხეთ მრეკავის რეჟიმის შესაცვლელად"</string>
     <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"დადუმება"</string>
     <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"დადუმების მოხსნა"</string>
     <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"ვიბრაცია"</string>
@@ -873,9 +872,12 @@
     <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_full_screen" msgid="336048080383640562">"გადართვა ეკრანის გაყოფიდან სრულ ეკრანზე"</string>
+    <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+    <skip />
+    <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+    <skip />
+    <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+    <skip />
     <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>
@@ -1438,8 +1440,7 @@
     <string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"მოქმედების ან მეტა კლავიშის ხატულა"</string>
     <string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"პლუსის ხატულა"</string>
     <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"მორგება"</string>
-    <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
-    <skip />
+    <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"გადაყენება"</string>
     <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"მზადაა"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ხატულის გაფართოება"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ან"</string>
@@ -1481,8 +1482,7 @@
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"დააჭირეთ მოქმედების კლავიშს თქვენს კლავიატურაზე"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"ყოჩაღ!"</string>
     <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"თქვენ დაასრულეთ ყველა აპის ნახვის ჟესტი"</string>
-    <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
-    <skip />
+    <string name="tutorial_animation_content_description" msgid="2698816574982370184">"სახელმძღვანელო ანიმაცია, დააწკაპუნეთ დასაპაუზებლად და გასაგრძელებლად."</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"კლავიატურის შენათება"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"დონე: %1$d %2$d-დან"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"სახლის კონტროლი"</string>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index 672bb66..9b6dc95 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -531,6 +531,8 @@
     <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Виджеттер"</string>
     <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"\"Виджеттер\" таңбашасын қосу үшін параметрлерде \"Виджеттерді құлыптаулы экранда көрсету\" опциясының қосулы екенін тексеріңіз."</string>
     <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Параметрлер"</string>
+    <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
+    <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Пайдаланушыны ауыстыру"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ашылмалы мәзір"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Осы сеанстағы барлық қолданба мен дерек жойылады."</string>
@@ -591,8 +593,7 @@
     <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>
-    <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
-    <skip />
+    <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Хабарландыру параметрлерін ашу"</string>
     <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Хабарландырулар Мазаламау режимінде кідіртілді"</string>
     <string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Хабарландырулар жоқ.}=1{Хабарландыруларды {mode} режимі кідіртті.}=2{Хабарландыруларды {mode} және тағы бір режим кідіртті.}other{Хабарландыруларды {mode} және тағы # режим кідіртті.}}"</string>
     <string name="media_projection_action_text" msgid="3634906766918186440">"Қазір бастау"</string>
@@ -707,8 +708,7 @@
     <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Бас қимылын қадағалау"</string>
     <string name="volume_ringer_change" msgid="3574969197796055532">"Қоңырау режимін өзгерту үшін түртіңіз."</string>
     <string name="volume_ringer_mode" msgid="6867838048430807128">"қоңырау режимі"</string>
-    <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
-    <skip />
+    <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, қоңырау режимін өзгерту үшін түртіңіз."</string>
     <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"дыбысын өшіру"</string>
     <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"дыбысын қосу"</string>
     <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"дірілдету"</string>
@@ -873,9 +873,12 @@
     <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_full_screen" msgid="336048080383640562">"Бөлінген экран режимінен толық экран режиміне ауысу"</string>
+    <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+    <skip />
+    <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+    <skip />
+    <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+    <skip />
     <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>
@@ -1480,8 +1483,7 @@
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Пернетақтадағы әрекет пернесін басыңыз."</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Жарайсыз!"</string>
     <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Барлық қолданбаны көру қимылын орындадыңыз."</string>
-    <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
-    <skip />
+    <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Оқулықтың анимациясы, ойнатуды кідірту және жалғастыру үшін басыңыз."</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Пернетақта жарығы"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Деңгей: %1$d/%2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Үй басқару элементтері"</string>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index 96a9ed4..50f5fa0 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -531,6 +531,7 @@
     <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"ធាតុ​ក្រាហ្វិក"</string>
     <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"ដើម្បីបញ្ចូលផ្លូវកាត់ \"ធាតុ​ក្រាហ្វិក\" ត្រូវប្រាកដថា \"បង្ហាញធាតុ​ក្រាហ្វិកនៅលើអេក្រង់ចាក់សោ\" ត្រូវបានបើកនៅក្នុងការកំណត់។"</string>
     <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"ការកំណត់"</string>
+    <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"បង្ហាញប៊ូតុងធាតុ​រក្សា​អេក្រង់"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ប្ដូរ​អ្នក​ប្រើ"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ម៉ឺនុយ​ទាញចុះ"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"កម្មវិធី និងទិន្នន័យ​ទាំងអស់​ក្នុង​វគ្គ​នេះ​នឹង​ត្រូវ​លុប។"</string>
@@ -591,8 +592,7 @@
     <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>
-    <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
-    <skip />
+    <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"បើកការកំណត់ការជូនដំណឹង"</string>
     <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"ការជូនដំណឹង​បានផ្អាក​ដោយ​មុខងារកុំរំខាន"</string>
     <string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{គ្មាន​ការជូន​ដំណឹង}=1{ការជូនដំណឹង​ត្រូវបាន​ផ្អាកដោយ {mode}}=2{ការជូនដំណឹង​ត្រូវបាន​ផ្អាកដោយ {mode} និង​មុខងារមួយ​ផ្សេងទៀត}other{ការជូនដំណឹង​ត្រូវបាន​ផ្អាកដោយ {mode} និង​មុខងារ # ផ្សេងទៀត}}"</string>
     <string name="media_projection_action_text" msgid="3634906766918186440">"ចាប់ផ្ដើម​ឥឡូវ"</string>
@@ -707,8 +707,7 @@
     <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"រេតាមក្បាល"</string>
     <string name="volume_ringer_change" msgid="3574969197796055532">"ចុច​ដើម្បីប្ដូរ​មុខងារ​រោទ៍"</string>
     <string name="volume_ringer_mode" msgid="6867838048430807128">"មុខងារកម្មវិធី​រោទ៍"</string>
-    <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
-    <skip />
+    <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g> ចុចដើម្បីប្ដូរមុខងារកម្មវិធី​រោទ៍"</string>
     <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"បិទ​សំឡេង"</string>
     <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"បើក​សំឡេង"</string>
     <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"ញ័រ"</string>
@@ -873,9 +872,12 @@
     <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_full_screen" msgid="336048080383640562">"ប្ដូរពីមុខងារ​បំបែកអេក្រង់ទៅជាអេក្រង់ពេញ"</string>
+    <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+    <skip />
+    <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+    <skip />
+    <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+    <skip />
     <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>
@@ -1421,7 +1423,7 @@
     <string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"កម្មវិធី​ប្រព័ន្ធ"</string>
     <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"ការធ្វើកិច្ចការច្រើន"</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>
@@ -1438,8 +1440,7 @@
     <string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"រូបគ្រាប់ចុចសកម្មភាព ឬមេតា"</string>
     <string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"រូបសញ្ញាបូក"</string>
     <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"ប្ដូរ​តាម​បំណង"</string>
-    <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
-    <skip />
+    <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"កំណត់​ឡើងវិញ"</string>
     <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"រួចរាល់"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"រូបតំណាង \"ពង្រីក\""</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ឬ"</string>
@@ -1481,8 +1482,7 @@
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"ចុចគ្រាប់ចុចសកម្មភាពលើក្ដារចុចរបស់អ្នក"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"ធ្វើបាន​ល្អ!"</string>
     <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"អ្នកបានបញ្ចប់ចលនាមើលកម្មវិធីទាំងអស់ហើយ"</string>
-    <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
-    <skip />
+    <string name="tutorial_animation_content_description" msgid="2698816574982370184">"រូបមានចលនាក្នុងអំឡុងមេរៀន ចុចដើម្បីផ្អាក រួចបន្តការចាក់ឡើងវិញ។"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"ពន្លឺក្រោយក្ដារចុច"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"កម្រិតទី %1$d នៃ %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"ការគ្រប់គ្រង​ផ្ទះ"</string>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index 89b8db2..a3718d3 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -531,6 +531,7 @@
     <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"ವಿಜೆಟ್‌ಗಳು"</string>
     <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"\"ವಿಜೆಟ್‌ಗಳು\" ಶಾರ್ಟ್‌ಕಟ್ ಸೇರಿಸಲು, ಸೆಟ್ಟಿಂಗ್‌ಗಳಲ್ಲಿ \"ಲಾಕ್ ಸ್ಕ್ರೀನ್‌ನಲ್ಲಿ ವಿಜೆಟ್‌ಗಳನ್ನು ತೋರಿಸಿ\" ಅನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ ಎಂದು ಖಚಿತಪಡಿಸಿಕೊಳ್ಳಿ."</string>
     <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"ಸೆಟ್ಟಿಂಗ್‌ಗಳು"</string>
+    <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"ಸ್ಕ್ರೀನ್‌ಸೇವರ್ ಬಟನ್ ತೋರಿಸಿ"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ಬಳಕೆದಾರರನ್ನು ಬದಲಿಸಿ"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ಪುಲ್‌ಡೌನ್ ಮೆನು"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ಈ ಸೆಶನ್‌ನಲ್ಲಿನ ಎಲ್ಲಾ ಆ್ಯಪ್‌ಗಳು ಮತ್ತು ಡೇಟಾವನ್ನು ಅಳಿಸಲಾಗುತ್ತದೆ."</string>
@@ -591,8 +592,7 @@
     <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>
-    <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
-    <skip />
+    <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"ನೋಟಿಫಿಕೇಶನ್‌ಗಳ ಸೆಟ್ಟಿಂಗ್‌ಗಳನ್ನು ತೆರೆಯಿರಿ"</string>
     <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"ಅಡಚಣೆ ಮಾಡಬೇಡಿ ಎನ್ನುವ ಮೂಲಕ ಅಧಿಸೂಚನೆಗಳನ್ನು ವಿರಾಮಗೊಳಿಸಲಾಗಿದೆ"</string>
     <string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{ಯಾವುದೇ ನೋಟಿಫಿಕೇಶನ್‌ಗಳು ಇಲ್ಲ}=1{ನೋಟಿಫಿಕೇಶನ್‌ಗಳನ್ನು {mode} ವಿರಾಮಗೊಳಿಸಿದೆ}=2{ನೋಟಿಫಿಕೇಶನ್‌ಗಳನ್ನು {mode} ಹಾಗೂ ಇತರ ಒಂದು ಮೋಡ್ ವಿರಾಮಗೊಳಿಸಿವೆ}one{ನೋಟಿಫಿಕೇಶನ್‌ಗಳನ್ನು {mode} ಹಾಗೂ ಇತರ # ಮೋಡ್‌ಗಳು ವಿರಾಮಗೊಳಿಸಿವೆ}other{ನೋಟಿಫಿಕೇಶನ್‌ಗಳನ್ನು {mode} ಹಾಗೂ ಇತರ # ಮೋಡ್‌ಗಳು ವಿರಾಮಗೊಳಿಸಿವೆ}}"</string>
     <string name="media_projection_action_text" msgid="3634906766918186440">"ಈಗ ಪ್ರಾರಂಭಿಸಿ"</string>
@@ -707,8 +707,7 @@
     <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"ಹೆಡ್ ಟ್ರ್ಯಾಕಿಂಗ್"</string>
     <string name="volume_ringer_change" msgid="3574969197796055532">"ರಿಂಗರ್ ಮೋಡ್ ಬದಲಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
     <string name="volume_ringer_mode" msgid="6867838048430807128">"ರಿಂಗರ್ ಮೋಡ್"</string>
-    <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
-    <skip />
+    <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, ರಿಂಗರ್ ಮೋಡ್ ಅನ್ನು ಬದಲಾಯಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
     <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"ಮ್ಯೂಟ್ ಮಾಡಿ"</string>
     <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ಅನ್‌ಮ್ಯೂಟ್ ಮಾಡಿ"</string>
     <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"ವೈಬ್ರೇಟ್‌"</string>
@@ -873,9 +872,12 @@
     <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_full_screen" msgid="336048080383640562">"ಸ್ಕ್ರೀನ್ ಬೇರ್ಪಡಿಸಿ ಮೋಡ್‌ನಿಂದ ಪೂರ್ಣ ಸ್ಕ್ರೀನ್‌ಗೆ ಬದಲಿಸಿ"</string>
+    <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+    <skip />
+    <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+    <skip />
+    <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+    <skip />
     <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>
@@ -1480,8 +1482,7 @@
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"ನಿಮ್ಮ ಕೀಬೋರ್ಡ್‌ನಲ್ಲಿ ಆ್ಯಕ್ಷನ್‌ ಕೀಯನ್ನು ಒತ್ತಿ"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"ಭೇಷ್!"</string>
     <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"ನೀವು ಎಲ್ಲಾ ಆ್ಯಪ್‌ಗಳ ಜೆಸ್ಚರ್‌ ವೀಕ್ಷಣೆಯನ್ನು ಪೂರ್ಣಗೊಳಿಸಿದ್ದೀರಿ"</string>
-    <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
-    <skip />
+    <string name="tutorial_animation_content_description" msgid="2698816574982370184">"ಟ್ಯುಟೋರಿಯಲ್ ಅನಿಮೇಷನ್, ವಿರಾಮಗೊಳಿಸಲು ಮತ್ತು ಪ್ಲೇ ಪುನರಾರಂಭಿಸಲು ಕ್ಲಿಕ್ ಮಾಡಿ."</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"ಕೀಬೋರ್ಡ್ ಬ್ಯಾಕ್‌ಲೈಟ್"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d ರಲ್ಲಿ %1$d ಮಟ್ಟ"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"ಮನೆ ನಿಯಂತ್ರಣಗಳು"</string>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index 276d708..ee19ad2 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -531,6 +531,8 @@
     <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"위젯"</string>
     <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"\'위젯\' 바로가기를 추가하려면 설정에서 \'잠금 화면에 위젯 표시\'가 사용 설정되어 있어야 합니다."</string>
     <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"설정"</string>
+    <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
+    <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"사용자 전환"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"풀다운 메뉴"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"이 세션에 있는 모든 앱과 데이터가 삭제됩니다."</string>
@@ -591,8 +593,7 @@
     <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>
-    <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
-    <skip />
+    <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"알림 설정 열기"</string>
     <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"방해 금지 모드로 알림이 일시중지됨"</string>
     <string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{알림 없음}=1{{mode} 모드로 인해 알림이 일시중지되었습니다.}=2{{mode} 및 다른 모드로 인해 알림이 일시중지되었습니다.}other{{mode} 및 다른 모드 #개로 인해 알림이 일시중지되었습니다.}}"</string>
     <string name="media_projection_action_text" msgid="3634906766918186440">"시작하기"</string>
@@ -707,8 +708,7 @@
     <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"머리 추적"</string>
     <string name="volume_ringer_change" msgid="3574969197796055532">"탭하여 벨소리 장치 모드 변경"</string>
     <string name="volume_ringer_mode" msgid="6867838048430807128">"벨소리 장치 모드"</string>
-    <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
-    <skip />
+    <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, 탭하여 벨소리 장치 모드 변경"</string>
     <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"음소거"</string>
     <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"음소거 해제"</string>
     <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"진동"</string>
@@ -873,9 +873,12 @@
     <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_full_screen" msgid="336048080383640562">"화면 분할에서 전체 화면으로 전환"</string>
+    <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+    <skip />
+    <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+    <skip />
+    <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+    <skip />
     <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>
@@ -1438,8 +1441,7 @@
     <string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"작업 또는 메타 키 아이콘"</string>
     <string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"더하기 아이콘"</string>
     <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"맞춤설정"</string>
-    <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
-    <skip />
+    <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"재설정"</string>
     <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"완료"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"확장 아이콘"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"또는"</string>
@@ -1481,8 +1483,7 @@
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"키보드의 작업 키를 누르세요."</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"잘하셨습니다"</string>
     <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"모든 앱 보기 동작을 완료했습니다."</string>
-    <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
-    <skip />
+    <string name="tutorial_animation_content_description" msgid="2698816574982370184">"튜토리얼 애니메이션입니다. 일시중지하고 재생을 재개하려면 클릭하세요."</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"키보드 백라이트"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d단계 중 %1$d단계"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"홈 컨트롤"</string>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index ab20a7b..7ebfdec 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -531,6 +531,8 @@
     <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Виджеттер"</string>
     <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"\"Виджеттер\" ыкчам баскычын кошуу үчүн параметрлерге өтүп, \"Виджеттерди кулпуланган экранда көрсөтүү\" параметри иштетилгенин текшериңиз."</string>
     <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Параметрлер"</string>
+    <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
+    <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Колдонуучуну которуу"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ылдый түшүүчү меню"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Бул сеанстагы бардык колдонмолор жана аларга байланыштуу нерселер өчүрүлөт."</string>
@@ -591,8 +593,7 @@
     <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>
-    <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
-    <skip />
+    <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Билдирмелердин параметрлерин ачуу"</string>
     <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"\"Тынчымды алба\" режиминде билдирмелер тындырылды"</string>
     <string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Билдирмелер жок}=1{{mode} режими билдирмелерди тындырды}=2{{mode} жана дагы бир режим билдирмелерди тындырды}other{{mode} жана дагы # режим билдирмелерди тындырды}}"</string>
     <string name="media_projection_action_text" msgid="3634906766918186440">"Азыр баштоо"</string>
@@ -707,8 +708,7 @@
     <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Баштын кыймылына көз салуу"</string>
     <string name="volume_ringer_change" msgid="3574969197796055532">"Коңгуроо режимин өзгөртүү үчүн басыңыз"</string>
     <string name="volume_ringer_mode" msgid="6867838048430807128">"коңгуроо режими"</string>
-    <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
-    <skip />
+    <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, коңгуроо режимин өзгөртүү үчүн басыңыз"</string>
     <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"үнсүз"</string>
     <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"үнүн чыгаруу"</string>
     <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"дирилдөө"</string>
@@ -873,9 +873,12 @@
     <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_full_screen" msgid="336048080383640562">"Экранды бөлүү режиминен толук экранга которулуу"</string>
+    <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+    <skip />
+    <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+    <skip />
+    <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+    <skip />
     <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>
@@ -1438,8 +1441,7 @@
     <string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Аракет же Мета ачкыч сүрөтчөсү"</string>
     <string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Кошуу сүрөтчөсү"</string>
     <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Ыңгайлаштыруу"</string>
-    <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
-    <skip />
+    <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Баштапкы абалга келтирүү"</string>
     <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Бүттү"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Жайып көрсөтүү сүрөтчөсү"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"же"</string>
@@ -1481,8 +1483,7 @@
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Баскычтобуңуздагы аракет баскычын басыңыз"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Эң жакшы!"</string>
     <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Бардык колдонмолорду көрүү жаңсоосун аткардыңыз"</string>
-    <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
-    <skip />
+    <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Үйрөткүч анимация, ойнотууну тындыруу же улантуу үчүн чыкылдатыңыз."</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Баскычтоптун жарыгы"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d ичинен %1$d-деңгээл"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Үйдөгү түзмөктөрдү тескөө"</string>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index a3a25e8..151687f 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -529,10 +529,9 @@
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"ເພື່ອເປີດແອັບໂດຍໃຊ້ວິດເຈັດ, ທ່ານຈະຕ້ອງຢັ້ງຢືນວ່າແມ່ນທ່ານ. ນອກຈາກນັ້ນ, ກະລຸນາຮັບຊາບວ່າທຸກຄົນສາມາດເບິ່ງຂໍ້ມູນດັ່ງກ່າວໄດ້, ເຖິງແມ່ນວ່າແທັບເລັດຂອງທ່ານຈະລັອກຢູ່ກໍຕາມ. ວິດເຈັດບາງຢ່າງອາດບໍ່ໄດ້ມີໄວ້ສຳລັບໜ້າຈໍລັອກຂອງທ່ານ ແລະ ອາດບໍ່ປອດໄພທີ່ຈະເພີ່ມໃສ່ບ່ອນນີ້."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"ເຂົ້າໃຈແລ້ວ"</string>
     <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"ວິດເຈັດ"</string>
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
-    <skip />
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
-    <skip />
+    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"ເພື່ອເພີ່ມທາງລັດ \"ວິດເຈັດ\", ກະລຸນາກວດສອບວ່າໄດ້ເປີດການນຳໃຊ້ \"ສະແດງວິດເຈັດຢູ່ໜ້າຈໍລັອກ\" ໃນການຕັ້ງຄ່າແລ້ວ."</string>
+    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"ການຕັ້ງຄ່າ"</string>
+    <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"ປຸ່ມສະແດງພາບພັກໜ້າຈໍ"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ສະຫຼັບຜູ້ໃຊ້"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ເມນູແບບດຶງລົງ"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ແອັບຯ​ແລະ​ຂໍ້​ມູນ​ທັງ​ໝົດ​ໃນ​ເຊດ​ຊັນ​ນີ້​ຈະ​ຖືກ​ລຶບ​ອອກ."</string>
@@ -593,8 +592,7 @@
     <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>
-    <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
-    <skip />
+    <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"ເປີດການຕັ້ງຄ່າການແຈ້ງເຕືອນ"</string>
     <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"ຢຸດການແຈ້ງເຕືອນໂດຍໂໝດຫ້າມລົບກວນແລ້ວ"</string>
     <string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{ບໍ່ມີການແຈ້ງເຕືອນ}=1{{mode} ຢຸດການແຈ້ງເຕືອນໄວ້ຊົ່ວຄາວ}=2{{mode} ແລະ ອີກ 1 ໂໝດຢຸດການແຈ້ງເຕືອນໄວ້ຊົ່ວຄາວ}other{{mode} ແລະ ອີກ # ໂໝດຢຸດການແຈ້ງເຕືອນໄວ້ຊົ່ວຄາວ}}"</string>
     <string name="media_projection_action_text" msgid="3634906766918186440">"ເລີ່ມດຽວນີ້"</string>
@@ -709,8 +707,7 @@
     <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"ການຕິດຕາມຫົວ"</string>
     <string name="volume_ringer_change" msgid="3574969197796055532">"ແຕະເພື່ອປ່ຽນໂໝດຣິງເກີ"</string>
     <string name="volume_ringer_mode" msgid="6867838048430807128">"ໂໝດຣິງເກີ"</string>
-    <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
-    <skip />
+    <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, ແຕະເພື່ອປ່ຽນໂໝດຣິງເກີ"</string>
     <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"ປິດສຽງ"</string>
     <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ເຊົາປິດສຽງ"</string>
     <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"ສັ່ນເຕືອນ"</string>
@@ -875,9 +872,12 @@
     <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_full_screen" msgid="336048080383640562">"ສະຫຼັບຈາກແບ່ງໜ້າຈໍໄປເປັນເຕັມຈໍ"</string>
+    <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+    <skip />
+    <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+    <skip />
+    <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+    <skip />
     <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>
@@ -1430,20 +1430,17 @@
     <string name="shortcut_helper_title" msgid="8567500639300970049">"ຄີລັດ"</string>
     <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"ປັບແຕ່ງຄີລັດ"</string>
     <string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"ລຶບທາງລັດອອກບໍ?"</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"ຣີເຊັດກັບຄືນເປັນຄ່າເລີ່ມຕົ້ນບໍ?"</string>
     <string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"ກົດປຸ່ມເພື່ອກຳນົດທາງລັດ"</string>
     <string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"ການດຳເນີນການນີ້ຈະລຶບທາງລັດທີ່ກຳນົດເອງຂອງທ່ານຢ່າງຖາວອນ."</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"ການດຳເນີນການນີ້ຈະລຶບທາງລັດທີ່ກຳນົດເອງທັງໝົດຂອງທ່ານຢ່າງຖາວອນ."</string>
     <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>
     <string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"ໄອຄອນຄຳສັ່ງ ຫຼື ປຸ່ມ Meta"</string>
     <string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"ໄອຄອນໝາຍບວກ"</string>
     <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"ປັບແຕ່ງ"</string>
-    <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
-    <skip />
+    <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"ຣີເຊັດ"</string>
     <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"ແລ້ວໆ"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ໄອຄອນຂະຫຍາຍ"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ຫຼື"</string>
@@ -1453,8 +1450,7 @@
     <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"ການຕັ້ງຄ່າແປ້ນພິມ"</string>
     <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"ຕັ້ງທາງລັດ"</string>
     <string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"ລຶບອອກ"</string>
-    <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
-    <skip />
+    <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"ແມ່ນ, ຣີເຊັດ"</string>
     <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"ຍົກເລີກ"</string>
     <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"ກົດປຸ່ມ"</string>
     <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"ນໍາໃຊ້ປຸ່ມປະສົມຢູ່ແລ້ວ. ໃຫ້ລອງປຸ່ມອື່ນ."</string>
@@ -1486,8 +1482,7 @@
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"ກົດປຸ່ມຄຳສັ່ງຢູ່ແປ້ນພິມຂອງທ່ານ"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"ດີຫຼາຍ!"</string>
     <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"ທ່ານເບິ່ງທ່າທາງຂອງແອັບທັງໝົດສຳເລັດແລ້ວ"</string>
-    <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
-    <skip />
+    <string name="tutorial_animation_content_description" msgid="2698816574982370184">"ພາບເຄື່ອນໄຫວຂອງບົດສອນການນຳໃຊ້, ຄລິກເພື່ອຢຸດຊົ່ວຄາວ ແລະ ສືບຕໍ່ຫຼິ້ນ."</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"ໄຟປຸ່ມແປ້ນພິມ"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"ລະດັບທີ %1$d ຈາກ %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"ການຄວບຄຸມເຮືອນ"</string>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index cba0db1..91e17a7 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -529,10 +529,9 @@
     <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>
     <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Valdikliai"</string>
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
-    <skip />
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
-    <skip />
+    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Jei norite pridėti valdiklių šaukinį, patikrinkite, ar nustatymuose įgalinta parinktis „Rodyti valdiklius užrakinimo ekrane“."</string>
+    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Nustatymai"</string>
+    <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Mygtukas „Rodyti ekrano užsklandą“"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Perjungti naudotoją"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"išplečiamasis meniu"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Bus ištrintos visos šios sesijos programos ir duomenys."</string>
@@ -593,8 +592,7 @@
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Pranešimai"</string>
     <string name="notification_section_header_conversations" msgid="821834744538345661">"Pokalbiai"</string>
     <string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Išvalyti visus tylius pranešimus"</string>
-    <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
-    <skip />
+    <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Atidaryti pranešimų nustatymus"</string>
     <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Pranešimai pristabdyti naudojant netrukdymo režimą"</string>
     <string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Nėra pranešimų}=1{Pranešimai pristabdyti naudojant režimą „{mode}“}=2{Pranešimai pristabdyti naudojant režimą „{mode}“ ir dar vieną režimą}one{Pranešimai pristabdyti naudojant režimą „{mode}“ ir dar # režimą}few{Pranešimai pristabdyti naudojant režimą „{mode}“ ir dar # režimus}many{Pranešimai pristabdyti naudojant režimą „{mode}“ ir dar # režimo}other{Pranešimai pristabdyti naudojant režimą „{mode}“ ir dar # režimų}}"</string>
     <string name="media_projection_action_text" msgid="3634906766918186440">"Pradėti dabar"</string>
@@ -874,9 +872,12 @@
     <string name="group_system_lock_screen" msgid="7391191300363416543">"Užrakinti ekraną"</string>
     <string name="group_system_quick_memo" msgid="3764560265935722903">"Sukurti pastabą"</string>
     <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Kelių užduočių atlikimas"</string>
-    <string name="system_multitasking_rhs" msgid="8714224917276297810">"Naudoti išskaidyto ekrano režimą su dabartine programa dešinėje"</string>
-    <string name="system_multitasking_lhs" msgid="8402954791206308783">"Naudoti išskaidyto ekrano režimą su dabartine programa kairėje"</string>
-    <string name="system_multitasking_full_screen" msgid="336048080383640562">"Perjungti iš išskaidyto ekrano režimo į viso ekrano režimą"</string>
+    <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+    <skip />
+    <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+    <skip />
+    <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+    <skip />
     <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Perjunkite į programą dešinėje arba apačioje išskaidyto ekrano režimu"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Perjunkite į programą kairėje arba viršuje išskaidyto ekrano režimu"</string>
     <string name="system_multitasking_replace" msgid="7410071959803642125">"Išskaidyto ekrano režimu: pakeisti iš vienos programos į kitą"</string>
@@ -1429,20 +1430,17 @@
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Spartieji klavišai"</string>
     <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Sparčiųjų klavišų tinkinimas"</string>
     <string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Pašalinti spartųjį klavišą?"</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Iš naujo nustatyti numatytąjį nustatymą?"</string>
     <string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Paspauskite klavišą, kad priskirtumėte spartųjį klavišą"</string>
     <string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Bus visam laikui ištrintas tinkintas spartusis klavišas."</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Bus visam laikui ištrinti visi tinkinti šaukiniai."</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Ieškoti sparčiųjų klavišų"</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>
     <string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Veiksmo arba metaduomenų klavišo piktograma"</string>
     <string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Pliuso piktograma"</string>
     <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Tinkinti"</string>
-    <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
-    <skip />
+    <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Nustatyti iš naujo"</string>
     <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Atlikta"</string>
     <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>
@@ -1452,8 +1450,7 @@
     <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Klaviatūros nustatymai"</string>
     <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Nustatyti spartųjį klavišą"</string>
     <string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Pašalinti"</string>
-    <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
-    <skip />
+    <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Taip, nustatyti iš naujo"</string>
     <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Atšaukti"</string>
     <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Paspauskite klavišą"</string>
     <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Klavišų derinys jau naudojamas. Bandykite naudoti kitą klavišą."</string>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index 7698008..c06c2ba 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -529,9 +529,9 @@
     <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>
     <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Logrīki"</string>
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
-    <skip />
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
+    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Lai pievienotu saīsni “Logrīki”, iestatījumos noteikti iespējojiet opciju “Rādīt logrīkus bloķēšanas ekrānā”."</string>
+    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Iestatījumi"</string>
+    <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
     <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Mainīt lietotāju"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"novelkamā izvēlne"</string>
@@ -593,8 +593,7 @@
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Paziņojumi"</string>
     <string name="notification_section_header_conversations" msgid="821834744538345661">"Sarunas"</string>
     <string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Notīrīt visus klusos paziņojumus"</string>
-    <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
-    <skip />
+    <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Atvērt paziņojumu iestatījumus"</string>
     <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Paziņojumi pārtraukti, izmantojot iestatījumu “Netraucēt”"</string>
     <string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Nav paziņojumu}=1{Paziņojumu rādīšana ir pārtraukta režīma “{mode}” dēļ}=2{Paziņojumu rādīšana ir pārtraukta režīma “{mode}” un vēl viena režīma dēļ}zero{Paziņojumu rādīšana ir pārtraukta režīma “{mode}” un vēl # režīmu dēļ}one{Paziņojumu rādīšana ir pārtraukta režīma “{mode}” un vēl # režīma dēļ}other{Paziņojumu rādīšana ir pārtraukta režīma “{mode}” un vēl # režīmu dēļ}}"</string>
     <string name="media_projection_action_text" msgid="3634906766918186440">"Sākt tūlīt"</string>
@@ -709,8 +708,7 @@
     <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Seko galvai"</string>
     <string name="volume_ringer_change" msgid="3574969197796055532">"Pieskarieties, lai mainītu zvanītāja režīmu."</string>
     <string name="volume_ringer_mode" msgid="6867838048430807128">"zvanītāja režīms"</string>
-    <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
-    <skip />
+    <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>; pieskarieties, lai mainītu zvanītāja režīmu"</string>
     <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"izslēgt skaņu"</string>
     <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ieslēgt skaņu"</string>
     <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrēt"</string>
@@ -875,9 +873,12 @@
     <string name="group_system_lock_screen" msgid="7391191300363416543">"Bloķēt ekrānu"</string>
     <string name="group_system_quick_memo" msgid="3764560265935722903">"Izveidot piezīmi"</string>
     <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Vairākuzdevumu režīms"</string>
-    <string name="system_multitasking_rhs" msgid="8714224917276297810">"Izmantot ekrāna sadalīšanu ar pašreizējo lietotni labajā pusē"</string>
-    <string name="system_multitasking_lhs" msgid="8402954791206308783">"Izmantot ekrāna sadalīšanu ar pašreizējo lietotni kreisajā pusē"</string>
-    <string name="system_multitasking_full_screen" msgid="336048080383640562">"Pārslēgties no ekrāna sadalīšanas režīma uz pilnekrāna režīmu"</string>
+    <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+    <skip />
+    <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+    <skip />
+    <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+    <skip />
     <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Pāriet uz lietotni pa labi/lejā, kamēr izmantojat sadalīto ekrānu."</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Pāriet uz lietotni pa kreisi/augšā, kamēr izmantojat sadalīto ekrānu."</string>
     <string name="system_multitasking_replace" msgid="7410071959803642125">"Ekrāna sadalīšanas režīmā: pārvietot lietotni no viena ekrāna uz otru"</string>
@@ -1430,20 +1431,17 @@
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Īsinājumtaustiņi"</string>
     <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Īsinājumtaustiņu pielāgošana"</string>
     <string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Vai noņemt saīsni?"</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Vai atiestatīt noklusējuma vērtības?"</string>
     <string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Lai piešķirtu īsinājumtaustiņu, nospiediet taustiņu"</string>
     <string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Veicot šo darbību, jūsu pielāgotā saīsne tiks neatgriezeniski izdzēsta."</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Veicot šo darbību, visas jūsu pielāgotās saīsnes tiks neatgriezeniski dzēstas."</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Meklēt 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>
     <string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Darbību jeb meta taustiņa ikona"</string>
     <string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Pluszīmes ikona"</string>
     <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Pielāgot"</string>
-    <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
-    <skip />
+    <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Atiestatīt"</string>
     <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Gatavs"</string>
     <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>
@@ -1453,8 +1451,7 @@
     <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Tastatūras iestatījumi"</string>
     <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Iestatīt īsinājumtaustiņu"</string>
     <string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Noņemt"</string>
-    <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
-    <skip />
+    <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Jā, atiestatīt"</string>
     <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Atcelt"</string>
     <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Nospiediet taustiņu"</string>
     <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Taustiņu kombinācija jau tiek izmantota. Izmēģiniet citu taustiņu."</string>
@@ -1486,8 +1483,7 @@
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Tastatūrā nospiediet darbību taustiņu."</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Lieliski!"</string>
     <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Jūs sekmīgi veicāt visu lietotņu skatīšanas žestu."</string>
-    <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
-    <skip />
+    <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Mācību animācija. Noklikšķiniet, lai pārtrauktu un atsāktu atskaņošanu."</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Tastatūras fona apgaismojums"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Līmenis numur %1$d, kopā ir %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Mājas kontrolierīces"</string>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index 783e52e..a1e6017 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -529,9 +529,9 @@
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"За да отворите апликација со помош на виџет, ќе треба да потврдите дека сте вие. Покрај тоа, имајте предвид дека секој може да ги гледа виџетите, дури и кога вашиот таблет е заклучен. Некои виџети можеби не се наменети за вашиот заклучен екран, па можеби не е безбедно да се додадат овде."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Сфатив"</string>
     <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Виџети"</string>
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
-    <skip />
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
+    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"За да ја додадете кратенката „Виџети“, погрижете се да биде овозможен „Прикажување виџети на заклучен екран“ во „Поставки“."</string>
+    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Поставки"</string>
+    <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
     <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Промени го корисникот"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"паѓачко мени"</string>
@@ -709,8 +709,7 @@
     <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Следење на главата"</string>
     <string name="volume_ringer_change" msgid="3574969197796055532">"Допрете за да го промените режимот на ѕвончето"</string>
     <string name="volume_ringer_mode" msgid="6867838048430807128">"режим на ѕвонче"</string>
-    <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
-    <skip />
+    <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, допрете за да го промените режимот на ѕвончето"</string>
     <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"исклучен звук"</string>
     <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"вклучен звук"</string>
     <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"вибрации"</string>
@@ -875,9 +874,12 @@
     <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_full_screen" msgid="336048080383640562">"Префрлете се од поделен екран на цел екран"</string>
+    <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+    <skip />
+    <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+    <skip />
+    <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+    <skip />
     <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>
@@ -887,8 +889,8 @@
     <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Максимизирај го прозорецот"</string>
     <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Минимизирај го прозорецот"</string>
     <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Внесување"</string>
-    <string name="input_switch_input_language_next" msgid="3782155659868227855">"Префрлете на следниот јазик"</string>
-    <string name="input_switch_input_language_previous" msgid="6043341362202336623">"Префрлете на претходниот јазик"</string>
+    <string name="input_switch_input_language_next" msgid="3782155659868227855">"Префрлете се на следниот јазик"</string>
+    <string name="input_switch_input_language_previous" msgid="6043341362202336623">"Префрлете се на претходниот јазик"</string>
     <string name="input_access_emoji" msgid="8105642858900406351">"Пристапете до емоџијата"</string>
     <string name="input_access_voice_typing" msgid="7291201476395326141">"Пристапете до гласовното пишување"</string>
     <string name="keyboard_shortcut_group_applications" msgid="7386239431100651266">"Апликации"</string>
@@ -1430,20 +1432,17 @@
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Кратенки од тастатура"</string>
     <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Приспособете ги кратенките од тастатурата"</string>
     <string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Да се отстрани кратенката?"</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Да се ресетира на стандардните поставки?"</string>
     <string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Притиснете го копчето за да доделите кратенка"</string>
     <string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Ова ќе ја избрише вашата приспособена кратенка трајно."</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Ова ќе ги избрише трајно сите ваши приспособени кратенки."</string>
     <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>
     <string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Икона за дејство или копче за дејство"</string>
     <string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Икона плус"</string>
     <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Приспособете"</string>
-    <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
-    <skip />
+    <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Ресетирај"</string>
     <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Готово"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Икона за проширување"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"или"</string>
@@ -1453,8 +1452,7 @@
     <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Поставки за тастатурата"</string>
     <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Поставете кратенка"</string>
     <string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Отстрани"</string>
-    <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
-    <skip />
+    <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Да, ресетирај"</string>
     <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Откажи"</string>
     <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Притиснете го копчето"</string>
     <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Комбинацијата на копчиња веќе се користи. Обидете се со друго копче."</string>
@@ -1486,8 +1484,7 @@
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Притиснете го копчето за дејство на тастатурата"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Браво!"</string>
     <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Го завршивте движењето за прегледување на сите апликации"</string>
-    <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
-    <skip />
+    <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Анимација за упатство, кликнете за ја паузирате и да ја продолжите репродукцијата."</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Осветлување на тастатура"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Ниво %1$d од %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Контроли за домот"</string>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index 3134872..4dc40f8 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -531,6 +531,7 @@
     <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"വിജറ്റുകൾ"</string>
     <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"\"വിജറ്റുകൾ\" കുറുക്കുവഴി ചേർക്കാൻ, ക്രമീകരണത്തിൽ \"ലോക്ക് സ്‌ക്രീനിൽ വിജറ്റുകൾ കാണിക്കുക\" പ്രവർത്തനക്ഷമമാക്കിയെന്ന് ഉറപ്പാക്കുക."</string>
     <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"ക്രമീകരണം"</string>
+    <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"സ്‌ക്രീൻ സേവർ ബട്ടൺ കാണിക്കുക"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ഉപയോക്താവ് മാറുക"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"പുൾഡൗൺ മെനു"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ഈ സെഷനിലെ എല്ലാ ആപ്പുകളും ഡാറ്റയും ഇല്ലാതാക്കും."</string>
@@ -591,8 +592,7 @@
     <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>
-    <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
-    <skip />
+    <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"അറിയിപ്പ് ക്രമീകരണം തുറക്കുക"</string>
     <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"\'ശല്യപ്പെടുത്തരുത്\' വഴി അറിയിപ്പുകൾ താൽക്കാലികമായി നിർത്തി"</string>
     <string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{അറിയിപ്പുകൾ ഒന്നുമില്ല}=1{{mode}, അറിയിപ്പുകൾ താൽക്കാലികമായി നിർത്തിയിരിക്കുന്നു}=2{{mode} എന്നതും മറ്റൊരു മോഡും, അറിയിപ്പുകൾ താൽക്കാലികമായി നിർത്തിയിരിക്കുന്നു}other{{mode} എന്നതും മറ്റ് # മോഡുകളും, അറിയിപ്പുകൾ താൽക്കാലികമായി നിർത്തിയിരിക്കുന്നു}}"</string>
     <string name="media_projection_action_text" msgid="3634906766918186440">"ഇപ്പോൾ ആരംഭിക്കുക"</string>
@@ -707,8 +707,7 @@
     <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"ഹെഡ് ട്രാക്കിംഗ്"</string>
     <string name="volume_ringer_change" msgid="3574969197796055532">"റിംഗർ മോഡ് മാറ്റാൻ ടാപ്പ് ചെയ്യുക"</string>
     <string name="volume_ringer_mode" msgid="6867838048430807128">"റിംഗർ മോഡ്"</string>
-    <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
-    <skip />
+    <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, റിംഗർ മോഡ് മാറ്റാൻ ടാപ്പ് ചെയ്യുക"</string>
     <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"മ്യൂട്ട് ചെയ്യുക"</string>
     <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"അൺമ്യൂട്ട് ചെയ്യുക"</string>
     <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"വൈബ്രേറ്റ് ചെയ്യുക"</string>
@@ -873,9 +872,12 @@
     <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_full_screen" msgid="336048080383640562">"സ്‌ക്രീൻ വിഭജന മോഡിൽ നിന്ന് പൂർണ്ണ സ്ക്രീനിലേക്ക് മാറുക"</string>
+    <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+    <skip />
+    <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+    <skip />
+    <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+    <skip />
     <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>
@@ -1438,8 +1440,7 @@
     <string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"ആക്ഷൻ/മെറ്റാ കീ ഐക്കൺ"</string>
     <string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"പ്ലസ് ഐക്കൺ"</string>
     <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"ഇഷ്‌ടാനുസൃതമാക്കുക"</string>
-    <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
-    <skip />
+    <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"റീസെറ്റ് ചെയ്യുക"</string>
     <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"പൂർത്തിയായി"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"വികസിപ്പിക്കൽ ഐക്കൺ"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"അല്ലെങ്കിൽ"</string>
@@ -1481,8 +1482,7 @@
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"നിങ്ങളുടെ കീബോർഡിലെ ആക്ഷൻ കീ അമർത്തുക"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"അഭിനന്ദനങ്ങൾ!"</string>
     <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"\'എല്ലാ ആപ്പുകളും കാണുക\' ജെസ്ച്ചർ നിങ്ങൾ പൂർത്തിയാക്കി"</string>
-    <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
-    <skip />
+    <string name="tutorial_animation_content_description" msgid="2698816574982370184">"ട്യൂട്ടോറിയൽ ആനിമേഷൻ, താൽക്കാലികമായി നിർത്താനും പ്ലേ പുനരാരംഭിക്കാനും ക്ലിക്ക് ചെയ്യുക."</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"കീബോഡ് ബാക്ക്‌ലൈറ്റ്"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d-ൽ %1$d-ാമത്തെ ലെവൽ"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"ഹോം കൺട്രോളുകൾ"</string>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index 42182bf..1343634 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -529,9 +529,9 @@
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Виджет ашиглан аппыг нээхийн тулд та өөрийгөө мөн болохыг баталгаажуулах шаардлагатай болно. Мөн таны таблет түгжээтэй байсан ч тэдгээрийг дурын хүн үзэж болохыг санаарай. Зарим виджет таны түгжээтэй дэлгэцэд зориулагдаагүй байж магадгүй ба энд нэмэхэд аюултай байж болзошгүй."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Ойлголоо"</string>
     <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Виджет"</string>
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
-    <skip />
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
+    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"\"Виджет\"-ийн товчлол нэмэхийн тулд \"Түгжээтэй дэлгэц дээр виджет харуулах\"-ыг тохиргоонд идэвхжүүлсэн эсэхийг нягтална уу."</string>
+    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Тохиргоо"</string>
+    <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
     <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Хэрэглэгчийг сэлгэх"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"эвхмэл цэс"</string>
@@ -593,8 +593,7 @@
     <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>
-    <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
-    <skip />
+    <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Мэдэгдлийн тохиргоог нээнэ"</string>
     <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Бүү саад бол горимын түр зогсоосон мэдэгдэл"</string>
     <string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Мэдэгдэл байхгүй}=1{Мэдэгдлийг {mode} түр зогсоосон}=2{Мэдэгдлийг {mode} болон өөр нэг горим түр зогсоосон}other{Мэдэгдлийг {mode} болон өөр # горим түр зогсоосон}}"</string>
     <string name="media_projection_action_text" msgid="3634906766918186440">"Одоо эхлүүлэх"</string>
@@ -709,8 +708,7 @@
     <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Толгой хянах"</string>
     <string name="volume_ringer_change" msgid="3574969197796055532">"Хонхны горимыг өөрчлөхийн тулд товшино уу"</string>
     <string name="volume_ringer_mode" msgid="6867838048430807128">"хонхны горим"</string>
-    <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
-    <skip />
+    <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, хонхны горимыг өөрчлөхийн тулд товшино уу"</string>
     <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"дууг хаах"</string>
     <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"дууг нээх"</string>
     <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"чичрэх"</string>
@@ -875,9 +873,12 @@
     <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_full_screen" msgid="336048080383640562">"Дэлгэц хуваахаас бүтэн дэлгэц рүү сэлгэх"</string>
+    <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+    <skip />
+    <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+    <skip />
+    <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+    <skip />
     <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>
@@ -1430,20 +1431,17 @@
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Товчлуурын шууд холбоос"</string>
     <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Товчлуурын шууд холбоосыг өөрчлөх"</string>
     <string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Товчлолыг хасах уу?"</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Өгөгдмөл рүү буцааж шинэчлэх үү?"</string>
     <string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Товчлол оноохын тулд товч дарна уу"</string>
     <string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Энэ нь таны захиалгат товчлолыг бүрмөсөн устгана."</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Энэ нь таны бүх захиалгат товчлолыг бүрмөсөн устгана."</string>
     <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>
     <string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Үйлдлийн товч буюу өөрөөр Мета товчийн дүрс тэмдэг"</string>
     <string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Нэмэх дүрс тэмдэг"</string>
     <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Өөрчлөх"</string>
-    <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
-    <skip />
+    <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Шинэчлэх"</string>
     <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Болсон"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Дэлгэх дүрс тэмдэг"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"эсвэл"</string>
@@ -1453,8 +1451,7 @@
     <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Гарын тохиргоо"</string>
     <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Товчлол тохируулах"</string>
     <string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Хасах"</string>
-    <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
-    <skip />
+    <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Тэгье, шинэчилье"</string>
     <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Цуцлах"</string>
     <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Товч дарна уу"</string>
     <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Товчийн хослолыг аль хэдийн ашиглаж байна. Өөр товч туршиж үзнэ үү."</string>
@@ -1486,8 +1483,7 @@
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Гар дээрх тусгай товчлуурыг дарна уу"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Сайн байна!"</string>
     <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Та бүх аппыг харах зангааг гүйцэтгэлээ"</string>
-    <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
-    <skip />
+    <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Зааврын анимаци, түр зогсоохын тулд товшиж, үргэлжлүүлэн тоглуулна уу."</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Гарын арын гэрэл"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d-с %1$d-р түвшин"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Гэрийн удирдлага"</string>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index b108ccc..3f902d4 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -531,6 +531,8 @@
     <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"विजेट"</string>
     <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"\"विजेट\" शॉर्टकट जोडण्यासाठी, सेटिंग्जमध्ये \"लॉक स्‍क्रीनवर विजेट दाखवा\" सुरू असल्याची खात्री करा."</string>
     <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"सेटिंग्ज"</string>
+    <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
+    <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"वापरकर्ता स्विच करा"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"पुलडाउन मेनू"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"या सत्रातील सर्व अ‍ॅप्स आणि डेटा हटवला जाईल."</string>
@@ -591,8 +593,7 @@
     <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>
-    <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
-    <skip />
+    <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"नोटिफिकेशन सेटिंग्ज उघडा"</string>
     <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"व्यत्यय आणून नकाद्वारे सूचना थांबवल्या"</string>
     <string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{कोणतेही नोटिफिकेशन नाही}=1{{mode} द्वारे नोटिफिकेशन थांबवली आहेत}=2{{mode} द्वारे आणि आणखी एका मोडद्वारे नोटिफिकेशन थांबवली आहेत}other{{mode} द्वारे आणि आणखी # मोडद्वारे नोटिफिकेशन थांबवली आहेत}}"</string>
     <string name="media_projection_action_text" msgid="3634906766918186440">"आता सुरू करा"</string>
@@ -707,8 +708,7 @@
     <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"हेड ट्रॅकिंग"</string>
     <string name="volume_ringer_change" msgid="3574969197796055532">"रिंगर मोड बदलण्यासाठी टॅप करा"</string>
     <string name="volume_ringer_mode" msgid="6867838048430807128">"रिंगर मोड"</string>
-    <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
-    <skip />
+    <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, रिंगर मोड बदलण्यासाठी टॅप करा"</string>
     <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"म्यूट करा"</string>
     <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"म्यूट काढून टाका"</string>
     <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"व्हायब्रेट करा"</string>
@@ -873,9 +873,12 @@
     <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_full_screen" msgid="336048080383640562">"स्प्लिट स्क्रीनवरून फुल स्क्रीनवर स्विच करा"</string>
+    <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+    <skip />
+    <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+    <skip />
+    <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+    <skip />
     <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>
@@ -1438,8 +1441,7 @@
     <string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"कृती किंवा मेटा की आयकन"</string>
     <string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"अधिक आयकन"</string>
     <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"कस्टमाइझ करा"</string>
-    <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
-    <skip />
+    <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"रीसेट करा"</string>
     <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"पूर्ण झाले"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"विस्तार करा आयकन"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"किंवा"</string>
@@ -1481,8 +1483,7 @@
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"तुमच्या कीबोर्डवर अ‍ॅक्शन की प्रेस करा"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"खूप छान!"</string>
     <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"तुम्ही ॲप्स पाहण्याचे जेश्चर पूर्ण केले आहे"</string>
-    <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
-    <skip />
+    <string name="tutorial_animation_content_description" msgid="2698816574982370184">"ट्यूटोरियल अ‍ॅनिमेशन थांबवण्यासाठी किंवा पुन्हा सुरू करण्यासाठी प्ले करा वर क्लिक करा."</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"कीबोर्ड बॅकलाइट"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d पैकी %1$d पातळी"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"होम कंट्रोल"</string>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index 6b9d6d6..0900288 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -531,6 +531,7 @@
     <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widget"</string>
     <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Untuk menambahkan pintasan \"Widget\", pastikan \"Tunjukkan widget pada skrin kunci\" didayakan dalam tetapan."</string>
     <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Tetapan"</string>
+    <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Tunjukkan butang penyelamat skrin"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Tukar pengguna"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menu tarik turun"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Semua apl dan data dalam sesi ini akan dipadam."</string>
@@ -591,8 +592,7 @@
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Pemberitahuan"</string>
     <string name="notification_section_header_conversations" msgid="821834744538345661">"Perbualan"</string>
     <string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Kosongkan semua pemberitahuan senyap"</string>
-    <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
-    <skip />
+    <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Buka tetapan pemberitahuan"</string>
     <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Pemberitahuan dijeda oleh Jangan Ganggu"</string>
     <string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Tiada pemberitahuan}=1{Pemberitahuan dijeda oleh {mode}}=2{Pemberitahuan dijeda oleh {mode} dan satu lagi mod yang lain}other{Pemberitahuan dijeda oleh {mode} dan # mod yang lain}}"</string>
     <string name="media_projection_action_text" msgid="3634906766918186440">"Mulakan sekarang"</string>
@@ -707,8 +707,7 @@
     <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Penjejakan Kepala"</string>
     <string name="volume_ringer_change" msgid="3574969197796055532">"Ketik untuk menukar mod pendering"</string>
     <string name="volume_ringer_mode" msgid="6867838048430807128">"mod pendering"</string>
-    <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
-    <skip />
+    <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, ketik untuk menukar mod pendering"</string>
     <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"redam"</string>
     <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"nyahredam"</string>
     <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"getar"</string>
@@ -873,9 +872,12 @@
     <string name="group_system_lock_screen" msgid="7391191300363416543">"Kunci skrin"</string>
     <string name="group_system_quick_memo" msgid="3764560265935722903">"Catat nota"</string>
     <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Berbilang tugas"</string>
-    <string name="system_multitasking_rhs" msgid="8714224917276297810">"Gunakan skrin pisah dengan apl semasa pada sebelah kanan"</string>
-    <string name="system_multitasking_lhs" msgid="8402954791206308783">"Gunakan skrin pisah dengan apl semasa pada sebelah kiri"</string>
-    <string name="system_multitasking_full_screen" msgid="336048080383640562">"Beralih daripada skrin pisah kepada skrin penuh"</string>
+    <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+    <skip />
+    <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+    <skip />
+    <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+    <skip />
     <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Tukar kepada apl di sebelah kanan/bawah semasa menggunakan skrin pisah"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Tukar kepada apl di sebelah kiri/atas semasa menggunakan skrin pisah"</string>
     <string name="system_multitasking_replace" msgid="7410071959803642125">"Semasa skrin pisah: gantikan apl daripada satu apl kepada apl lain"</string>
@@ -1480,8 +1482,7 @@
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Tekan kekunci tindakan pada papan kekunci anda"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Syabas!"</string>
     <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Anda telah melengkapkan gerak isyarat lihat semua apl"</string>
-    <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
-    <skip />
+    <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Animasi tutorial, klik untuk menjeda dan menyambung semula main."</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Cahaya latar papan kekunci"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Tahap %1$d daripada %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Kawalan Rumah"</string>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index 50f75ee..186b60f 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -529,10 +529,9 @@
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"ဝိဂျက်သုံး၍ အက်ပ်ဖွင့်ရန်အတွက် သင်ဖြစ်ကြောင်း အတည်ပြုရန်လိုသည်။ ထို့ပြင် သင့်တက်ဘလက် လော့ခ်ချထားချိန်၌ပင် မည်သူမဆို ၎င်းတို့ကို ကြည့်နိုင်ကြောင်း သတိပြုပါ။ ဝိဂျက်အချို့ကို လော့ခ်မျက်နှာပြင်အတွက် ရည်ရွယ်ထားခြင်း မရှိသဖြင့် ဤနေရာတွင် ထည့်ပါက မလုံခြုံနိုင်ပါ။"</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"နားလည်ပြီ"</string>
     <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"ဝိဂျက်များ"</string>
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
-    <skip />
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
-    <skip />
+    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"“ဝိဂျက်များ” ဖြတ်လမ်းလင့်ခ်ထည့်ရန်အတွက် ဆက်တင်များတွင် “လော့ခ်မျက်နှာပြင်ပေါ်၌ ဝိဂျက်များပြရန်” ကိုဖွင့်ထားကြောင်း သေချာပါစေ။"</string>
+    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"ဆက်တင်များ"</string>
+    <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"စခရင်နားချိန်ပုံ ပြရန်ခလုတ်"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"အသုံးပြုသူကို ပြောင်းလဲရန်"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ဆွဲချမီနူး"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ဒီချိတ်ဆက်မှု ထဲက အက်ပ်များ အားလုံး နှင့် ဒေတာကို ဖျက်ပစ်မည်။"</string>
@@ -593,8 +592,7 @@
     <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>
-    <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
-    <skip />
+    <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"အကြောင်းကြားချက် ဆက်တင်များ ဖွင့်ရန်"</string>
     <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"အကြောင်းကြားချက်များကို \'မနှောင့်ယှက်ရ\' က ခေတ္တရပ်ထားသည်"</string>
     <string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{အကြောင်းကြားချက် မရှိပါ}=1{{mode} က ခဏရပ်ထားသော အကြောင်းကြားချက်များ}=2{{mode} နှင့် အခြားမုဒ်တစ်ခုက ခဏရပ်ထားသော အကြောင်းကြားချက်များ}other{{mode} နှင့် အခြားမုဒ် # ခုက ခဏရပ်ထားသော အကြောင်းကြားချက်များ}}"</string>
     <string name="media_projection_action_text" msgid="3634906766918186440">"ယခု စတင်ပါ"</string>
@@ -709,8 +707,7 @@
     <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"ခေါင်းလှုပ်ရှားမှု"</string>
     <string name="volume_ringer_change" msgid="3574969197796055532">"ဖုန်းခေါ်သံမုဒ်သို့ ပြောင်းရန် တို့ပါ"</string>
     <string name="volume_ringer_mode" msgid="6867838048430807128">"အသံမြည်မုဒ်"</string>
-    <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
-    <skip />
+    <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>၊ ဖုန်းခေါ်သံမုဒ်သို့ ပြောင်းရန် တို့ပါ"</string>
     <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"အသံပိတ်ရန်"</string>
     <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"အသံဖွင့်ရန်"</string>
     <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"တုန်ခါမှု"</string>
@@ -875,9 +872,12 @@
     <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_full_screen" msgid="336048080383640562">"မျက်နှာပြင် ခွဲ၍ပြသမှုမှ မျက်နှာပြင်အပြည့်သို့ ပြောင်းရန်"</string>
+    <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+    <skip />
+    <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+    <skip />
+    <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+    <skip />
     <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>
@@ -1430,20 +1430,17 @@
     <string name="shortcut_helper_title" msgid="8567500639300970049">"လက်ကွက်ဖြတ်လမ်းများ"</string>
     <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"လက်ကွက်ဖြတ်လမ်းများကို စိတ်ကြိုက်လုပ်ခြင်း"</string>
     <string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"ဖြတ်လမ်းလင့်ခ် ဖယ်ရှားမလား။"</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"မူရင်းသို့ ပြန်လည်ပြင်ဆင်သတ်မှတ်မလား။"</string>
     <string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"ဖြတ်လမ်းလင့်ခ်သတ်မှတ်ရန် ကီးကို နှိပ်ပါ"</string>
     <string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"၎င်းသည် သင့်စိတ်ကြိုက် ဖြတ်လမ်းလင့်ခ်ကို အပြီးဖျက်ပါမည်။"</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"၎င်းသည် သင့်စိတ်ကြိုက်ဖြတ်လမ်းလင့်ခ်အားလုံးကို အပြီးဖျက်မည်။"</string>
     <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>
     <string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"လုပ်ဆောင်ချက် (သို့) Meta ကီးသင်္ကေတ"</string>
     <string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"အပေါင်းသင်္ကေတ"</string>
     <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"စိတ်ကြိုက်လုပ်ရန်"</string>
-    <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
-    <skip />
+    <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"ပြင်ဆင်သတ်မှတ်ရန်"</string>
     <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"ပြီးပြီ"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ပိုပြရန် သင်္ကေတ"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"သို့မဟုတ်"</string>
@@ -1453,8 +1450,7 @@
     <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"ကီးဘုတ်ဆက်တင်များ"</string>
     <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"ဖြတ်လမ်း သတ်မှတ်ရန်"</string>
     <string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"ဖယ်ရှားရန်"</string>
-    <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
-    <skip />
+    <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"ပြန်လည်ပြင်ဆင်သတ်မှတ်မည်"</string>
     <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"မလုပ်တော့"</string>
     <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"ကီးကို နှိပ်ပါ"</string>
     <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"ကီးပေါင်းစပ်ခြင်းကို သုံးနေပြီးဖြစ်သည်။ အခြားကီးကို စမ်းကြည့်ပါ။"</string>
@@ -1486,8 +1482,7 @@
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"ကီးဘုတ်တွင် လုပ်ဆောင်ချက်ကီး နှိပ်ပါ"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"အလွန်ကောင်းပါသည်။"</string>
     <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"အက်ပ်အားလုံးကို ကြည့်ခြင်းလက်ဟန် သင်ခန်းစာပြီးပါပြီ"</string>
-    <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
-    <skip />
+    <string name="tutorial_animation_content_description" msgid="2698816574982370184">"ရှင်းလင်းပို့ချချက် လှုပ်ရှားသက်ဝင်ပုံ၊ ခဏရပ်ပြီး ဆက်ဖွင့်ရန် နှိပ်ပါ။"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"ကီးဘုတ်နောက်မီး"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"အဆင့် %2$d အနက် %1$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"အိမ်ထိန်းချုပ်မှုများ"</string>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index f84353f..1ef544b 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -529,9 +529,9 @@
     <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>
     <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Moduler"</string>
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
-    <skip />
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
+    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"For å legge til «Moduler»-snarveien, sørg for at «Vis moduler på låseskjermen» er slått på i innstillingene."</string>
+    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Innstillinger"</string>
+    <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
     <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Bytt bruker"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"rullegardinmeny"</string>
@@ -593,8 +593,7 @@
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Varsler"</string>
     <string name="notification_section_header_conversations" msgid="821834744538345661">"Samtaler"</string>
     <string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Fjern alle lydløse varsler"</string>
-    <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
-    <skip />
+    <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Åpne varslingsinnstillingene"</string>
     <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Varsler er satt på pause av «Ikke forstyrr»"</string>
     <string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Ingen varsler}=1{Varsler er satt på pause av {mode}}=2{Varsler er satt på pause av {mode} og én modus til}other{Varsler er satt på pause av {mode} og # moduser til}}"</string>
     <string name="media_projection_action_text" msgid="3634906766918186440">"Start nå"</string>
@@ -709,8 +708,7 @@
     <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Hodesporing"</string>
     <string name="volume_ringer_change" msgid="3574969197796055532">"Trykk for å endre ringemodus"</string>
     <string name="volume_ringer_mode" msgid="6867838048430807128">"modus for ringeprogrammet"</string>
-    <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
-    <skip />
+    <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, trykk for å endre ringemodus"</string>
     <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"kutt lyden"</string>
     <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"slå på lyden"</string>
     <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrer"</string>
@@ -875,9 +873,12 @@
     <string name="group_system_lock_screen" msgid="7391191300363416543">"Låseskjerm"</string>
     <string name="group_system_quick_memo" msgid="3764560265935722903">"Ta et notat"</string>
     <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Multitasking"</string>
-    <string name="system_multitasking_rhs" msgid="8714224917276297810">"Bruk delt skjerm med den nåværende appen til høyre"</string>
-    <string name="system_multitasking_lhs" msgid="8402954791206308783">"Bruk delt skjerm med den nåværende appen til venstre"</string>
-    <string name="system_multitasking_full_screen" msgid="336048080383640562">"Bytt fra delt skjerm til fullskjerm"</string>
+    <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+    <skip />
+    <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+    <skip />
+    <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+    <skip />
     <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Bytt til appen til høyre eller under mens du bruker delt skjerm"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Bytt til appen til venstre eller over mens du bruker delt skjerm"</string>
     <string name="system_multitasking_replace" msgid="7410071959803642125">"I delt skjerm: Bytt ut en app"</string>
@@ -1430,20 +1431,17 @@
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Hurtigtaster"</string>
     <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Tilpass hurtigtastene"</string>
     <string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Vil du fjerne hurtigtasten?"</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Vil du tilbakestille til standard?"</string>
     <string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Trykk på en tast for å tilordne hurtigtasten"</string>
     <string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Dette fører til at den egendefinerte hurtigtasten slettes permanent."</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Dette fører til at alle de egendefinerte snarveiene dine slettes permanent."</string>
     <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>
     <string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Handlings- eller Meta-tast-ikon"</string>
     <string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Plussikon"</string>
     <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Tilpass"</string>
-    <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
-    <skip />
+    <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Tilbakestill"</string>
     <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Ferdig"</string>
     <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>
@@ -1453,8 +1451,7 @@
     <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Tastaturinnstillinger"</string>
     <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Angi hurtigtast"</string>
     <string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Fjern"</string>
-    <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
-    <skip />
+    <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Ja, tilbakestill"</string>
     <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Avbryt"</string>
     <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Trykk på tasten"</string>
     <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Tastekombinasjonen brukes allerede. Prøv en annen tast."</string>
@@ -1486,8 +1483,7 @@
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Trykk på handlingstasten på tastaturet"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Bra!"</string>
     <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Du har fullført bevegelsen for å se alle apper"</string>
-    <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
-    <skip />
+    <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Veiledningsanimasjon. Klikk for å sette avspillingen på pause og gjenoppta den."</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Bakgrunnslys for tastatur"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Nivå %1$d av %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Hjemkontroller"</string>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index 8fd8367..9a97010 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -529,10 +529,9 @@
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"विजेट प्रयोग गरी एप खोल्न तपाईंले आफ्नो पहिचान पुष्टि गर्नु पर्ने हुन्छ। साथै, तपाईंको ट्याब्लेट लक भएका बेला पनि सबै जनाले तिनलाई देख्न सक्छन् भन्ने कुरा ख्याल गर्नुहोस्। केही विजेटहरू लक स्क्रिनमा प्रयोग गर्ने उद्देश्यले नबनाइएका हुन सक्छन् र तिनलाई यहाँ हाल्नु सुरक्षित नहुन सक्छ।"</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"बुझेँ"</string>
     <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"विजेटहरू"</string>
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
-    <skip />
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
-    <skip />
+    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"\"विजेट\" सर्टकट हाल्न सेटिङमा \"लक स्क्रिनमा विजेट देखाउनुहोस्\" नामक विकल्प अन गरिएको छ भन्ने सुनिश्चित गर्नुहोस्।"</string>
+    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"सेटिङ"</string>
+    <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"स्क्रिनसेभर देखाउने बटन"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"प्रयोगकर्ता फेर्नुहोस्"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"पुलडाउन मेनु"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"यो सत्रमा भएका सबै एपहरू र डेटा मेटाइने छ।"</string>
@@ -593,8 +592,7 @@
     <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>
-    <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
-    <skip />
+    <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"नोटिफिकेसन सेटिङ खोल्नुहोस्"</string>
     <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"बाधा नपुऱ्याउनुहोस् नामक मोडमार्फत पज पारिएका सूचनाहरू"</string>
     <string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{कुनै पनि नोटिफिकेसन छैन}=1{{mode} ले गर्दा नोटिफिकेसनहरू पज गरिएका छन्}=2{{mode} र अन्य एक मोडले गर्दा नोटिफिकेसनहरू पज गरिएका छन्}other{{mode} र अन्य # वटा मोडले गर्दा नोटिफिकेसनहरू पज गरिएका छन्}}"</string>
     <string name="media_projection_action_text" msgid="3634906766918186440">"अहिले न"</string>
@@ -709,8 +707,7 @@
     <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"हेड ट्र्याकिङ"</string>
     <string name="volume_ringer_change" msgid="3574969197796055532">"रिङ्गर मोड बदल्न ट्याप गर्नुहोस्"</string>
     <string name="volume_ringer_mode" msgid="6867838048430807128">"घण्टी बजाउने मोड"</string>
-    <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
-    <skip />
+    <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, घण्टी बजाउने मोड बदल्न ट्याप गर्नुहोस्"</string>
     <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"म्युट गर्नुहोस्"</string>
     <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"अनम्युट गर्नुहोस्"</string>
     <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"कम्पन गर्नुहोस्"</string>
@@ -875,9 +872,12 @@
     <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_full_screen" msgid="336048080383640562">"स्प्लिट स्क्रिनको साटो फुल स्क्रिन प्रयोग गर्नुहोस्"</string>
+    <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+    <skip />
+    <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+    <skip />
+    <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+    <skip />
     <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>
@@ -1430,20 +1430,17 @@
     <string name="shortcut_helper_title" msgid="8567500639300970049">"किबोर्डका सर्टकटहरू"</string>
     <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"किबोर्डका सर्टकटहरू कस्टमाइज गर्नुहोस्"</string>
     <string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"सर्टकट हटाउने हो?"</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"सर्टकट रिसेट गरी डिफल्ट बनाउने हो?"</string>
     <string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"सर्टकट असाइन गर्न की थिच्नुहोस्"</string>
     <string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"यसो गर्नुभयो भने तपाईंको कस्टम सर्टकट सदाका लागि मेटिने छ।"</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"तपाईंले यसो गर्नुभयो भने तपाईंका सबै कस्टम सर्टकटहरू सदाका लागि मेटाइने छन्।"</string>
     <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>
     <string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"एक्सन वा Meta कीको आइकन"</string>
     <string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"प्लस आइकन"</string>
     <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"कस्टमाइज गर्नुहोस्"</string>
-    <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
-    <skip />
+    <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"रिसेट गर्नुहोस्"</string>
     <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"पूरा भयो"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"\"एक्स्पान्ड गर्नुहोस्\" आइकन"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"वा"</string>
@@ -1453,8 +1450,7 @@
     <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"किबोर्डसम्बन्धी सेटिङ"</string>
     <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"सर्टकट सेट गर्नुहोस्"</string>
     <string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"हटाउनुहोस्"</string>
-    <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
-    <skip />
+    <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"अँ, रिसेट गर्नुहोस्"</string>
     <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"रद्द गर्नुहोस्"</string>
     <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"की थिच्नुहोस्"</string>
     <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"यो की कम्बिनेसन प्रयोग गरिसकिएको छ। अर्कै की प्रयोग गरी हेर्नुहोस्।"</string>
@@ -1486,8 +1482,7 @@
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"आफ्नो किबोर्डमा भएको एक्सन की थिच्नुहोस्"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"स्याबास!"</string>
     <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"तपाईंले जेस्चर प्रयोग गरी सबै एपहरू हेर्ने तरिका सिक्नुभएको छ"</string>
-    <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
-    <skip />
+    <string name="tutorial_animation_content_description" msgid="2698816574982370184">"ट्युटोरियलको एनिमेसन, पज वा सुचारु गर्न क्लिक गर्नुहोस्।"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"किबोर्ड ब्याकलाइट"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d मध्ये %1$d औँ स्तर"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"होम कन्ट्रोलहरू"</string>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index d03b5fb..e7fe5366 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -531,6 +531,7 @@
     <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgets"</string>
     <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Als je de snelkoppeling Widgets wilt toevoegen, zorg je dat Widgets tonen op het vergrendelingsscherm aanstaat in de instellingen."</string>
     <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Instellingen"</string>
+    <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Knop Screensaver tonen"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Gebruiker wijzigen"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"pull-downmenu"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alle apps en gegevens in deze sessie worden verwijderd."</string>
@@ -591,8 +592,7 @@
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Meldingen"</string>
     <string name="notification_section_header_conversations" msgid="821834744538345661">"Gesprekken"</string>
     <string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Alle stille meldingen wissen"</string>
-    <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
-    <skip />
+    <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Instellingen voor meldingen openen"</string>
     <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Meldingen onderbroken door \'Niet storen\'"</string>
     <string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Geen meldingen}=1{Meldingen onderbroken door {mode}}=2{Meldingen onderbroken door {mode} en 1 andere modus}other{Meldingen onderbroken door {mode} en # andere modi}}"</string>
     <string name="media_projection_action_text" msgid="3634906766918186440">"Nu starten"</string>
@@ -707,8 +707,7 @@
     <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Hoofdtracking"</string>
     <string name="volume_ringer_change" msgid="3574969197796055532">"Tik om de beltoonmodus te wijzigen"</string>
     <string name="volume_ringer_mode" msgid="6867838048430807128">"beltoonmodus"</string>
-    <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
-    <skip />
+    <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, tik om de belsoftwaremodus te wijzigen"</string>
     <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"geluid uit"</string>
     <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"geluid aanzetten"</string>
     <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"trillen"</string>
@@ -873,9 +872,12 @@
     <string name="group_system_lock_screen" msgid="7391191300363416543">"Scherm vergrendelen"</string>
     <string name="group_system_quick_memo" msgid="3764560265935722903">"Notitie maken"</string>
     <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Multitasken"</string>
-    <string name="system_multitasking_rhs" msgid="8714224917276297810">"Gesplitst scherm gebruiken met de huidige app aan de rechterkant"</string>
-    <string name="system_multitasking_lhs" msgid="8402954791206308783">"Gesplitst scherm gebruiken met de huidige app aan de linkerkant"</string>
-    <string name="system_multitasking_full_screen" msgid="336048080383640562">"Van gesplitst scherm naar volledig scherm schakelen"</string>
+    <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+    <skip />
+    <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+    <skip />
+    <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+    <skip />
     <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Naar de app rechts of onderaan gaan als je een gesplitst scherm gebruikt"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Naar de app links of bovenaan gaan als je een gesplitst scherm gebruikt"</string>
     <string name="system_multitasking_replace" msgid="7410071959803642125">"Tijdens gesplitst scherm: een app vervangen door een andere"</string>
@@ -1438,8 +1440,7 @@
     <string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Icoon voor actie- of metatoets"</string>
     <string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Plusicoon"</string>
     <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Aanpassen"</string>
-    <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
-    <skip />
+    <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Resetten"</string>
     <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Klaar"</string>
     <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>
@@ -1481,8 +1482,7 @@
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Druk op de actietoets op het toetsenbord"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Goed gedaan!"</string>
     <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Je weet nu hoe je het gebaar Alle apps bekijken maakt"</string>
-    <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
-    <skip />
+    <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Tutorial-animatie, klik om het afspelen te onderbreken en te hervatten."</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Achtergrondverlichting van toetsenbord"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Niveau %1$d van %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Bediening voor in huis"</string>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index 91900e7..35704ea 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -529,10 +529,9 @@
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"ଏକ ୱିଜେଟ ବ୍ୟବହାର କରି ଗୋଟିଏ ଆପ ଖୋଲିବା ପାଇଁ ଏହା ଆପଣ ଅଟନ୍ତି ବୋଲି ଆପଣଙ୍କୁ ଯାଞ୍ଚ କରିବାକୁ ହେବ। ଆହୁରି ମଧ୍ୟ, ଆପଣଙ୍କ ଟାବଲେଟ ଲକ ଥିଲେ ମଧ୍ୟ ଯେ କୌଣସି ବ୍ୟକ୍ତି ଏହାକୁ ଭ୍ୟୁ କରିପାରିବେ ବୋଲି ମନେ ରଖନ୍ତୁ। କିଛି ୱିଜେଟ ଆପଣଙ୍କ ଲକ ସ୍କ୍ରିନ ପାଇଁ ଉଦ୍ଦିଷ୍ଟ ହୋଇନଥାଇପାରେ ଏବଂ ଏଠାରେ ଯୋଗ କରିବା ଅସୁରକ୍ଷିତ ହୋଇପାରେ।"</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"ବୁଝିଗଲି"</string>
     <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"ୱିଜେଟ"</string>
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
-    <skip />
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
-    <skip />
+    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"\"ୱିଜେଟ\" ସର୍ଟକଟ ଯୋଗ କରିବାକୁ ସେଟିଂସରେ \"ଲକ ସ୍କ୍ରିନରେ ୱିଜେଟଗୁଡ଼ିକୁ ଦେଖାନ୍ତୁ\"କୁ ସକ୍ଷମ କରାଯାଇଥିବା ସୁନିଶ୍ଚିତ କରନ୍ତୁ।"</string>
+    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"ସେଟିଂସ"</string>
+    <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"ସ୍କ୍ରିନସେଭର ବଟନ ଦେଖାନ୍ତୁ"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ୟୁଜର୍‍ ବଦଳାନ୍ତୁ"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ପୁଲଡାଉନ ମେନୁ"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ଏହି ସେସନର ସମସ୍ତ ଆପ୍‌ ଓ ଡାଟା ଡିଲିଟ୍‌ ହୋଇଯିବ।"</string>
@@ -593,8 +592,7 @@
     <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>
-    <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
-    <skip />
+    <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"ବିଜ୍ଞପ୍ତି ସେଟିଂସ ଖୋଲନ୍ତୁ"</string>
     <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"\"ବିରକ୍ତ କରନ୍ତୁ ନାହିଁ\" ବିକଳ୍ପ ଦ୍ୱାରା ବିଜ୍ଞପ୍ତି ପଜ୍‍ ହୋଇଛି"</string>
     <string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{କୌଣସି ବିଜ୍ଞପ୍ତି ନାହିଁ}=1{{mode} ଦ୍ୱାରା ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକୁ ବିରତ କରାଯାଇଛି}=2{{mode} ଏବଂ ଅନ୍ୟ ଏକ ମୋଡ ଦ୍ୱାରା ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକୁ ବିରତ କରାଯାଇଛି}other{{mode} ଏବଂ ଅନ୍ୟ # ମୋଡ ଦ୍ୱାରା ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକୁ ବିରତ କରାଯାଇଛି}}"</string>
     <string name="media_projection_action_text" msgid="3634906766918186440">"ବର୍ତ୍ତମାନ ଆରମ୍ଭ କରନ୍ତୁ"</string>
@@ -709,8 +707,7 @@
     <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"ହେଡ ଟ୍ରାକିଂ"</string>
     <string name="volume_ringer_change" msgid="3574969197796055532">"ରିଙ୍ଗର୍ ମୋଡ୍ ବଦଳାଇବାକୁ ଟାପ୍ କରନ୍ତୁ"</string>
     <string name="volume_ringer_mode" msgid="6867838048430807128">"ରିଂଗର ମୋଡ"</string>
-    <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
-    <skip />
+    <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, ରିଙ୍ଗର ମୋଡ ପରିବର୍ତ୍ତନ କରିବାକୁ ଟାପ କରନ୍ତୁ"</string>
     <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"ମ୍ୟୁଟ"</string>
     <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ଅନ୍‍-ମ୍ୟୁଟ୍ କରନ୍ତୁ"</string>
     <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"ଭାଇବ୍ରେଟ୍"</string>
@@ -875,9 +872,12 @@
     <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_full_screen" msgid="336048080383640562">"ସ୍ପ୍ଲିଟ ସ୍କ୍ରିନରୁ ପୂର୍ଣ୍ଣ ସ୍କ୍ରିନକୁ ସୁଇଚ କରନ୍ତୁ"</string>
+    <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+    <skip />
+    <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+    <skip />
+    <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+    <skip />
     <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>
@@ -1430,20 +1430,17 @@
     <string name="shortcut_helper_title" msgid="8567500639300970049">"କୀବୋର୍ଡ ସର୍ଟକଟ"</string>
     <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"କୀବୋର୍ଡ ସର୍ଟକଟଗୁଡ଼ିକୁ କଷ୍ଟମାଇଜ କରନ୍ତୁ"</string>
     <string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"ସର୍ଟକଟକୁ କାଢ଼ି ଦେବେ?"</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"ଡିଫଲ୍ଟରେ ପୁଣି ରିସେଟ କରିବେ?"</string>
     <string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"ସର୍ଟକଟ ଆସାଇନ କରିବା ପାଇଁ କୀ\'କୁ ଦବାନ୍ତୁ"</string>
     <string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"ଏହା ଆପଣଙ୍କ କଷ୍ଟମ ସର୍ଟକଟକୁ ସ୍ଥାୟୀ ଭାବେ ଡିଲିଟ କରିଦେବ।"</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"ଏହା ଆପଣଙ୍କର ସମସ୍ତ କଷ୍ଟମ ସର୍ଟକଟକୁ ସ୍ଥାୟୀ ଭାବେ ଡିଲିଟ କରିଦେବ।"</string>
     <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>
     <string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"ଆକ୍ସନ କିମ୍ବା ମେଟା କୀ ଆଇକନ"</string>
     <string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"ପ୍ଲସ ଆଇକନ"</string>
     <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"କଷ୍ଟମାଇଜ କରନ୍ତୁ"</string>
-    <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
-    <skip />
+    <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"ରିସେଟ କରନ୍ତୁ"</string>
     <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"ହୋଇଗଲା"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ଆଇକନକୁ ବିସ୍ତାର କରନ୍ତୁ"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"କିମ୍ବା"</string>
@@ -1453,8 +1450,7 @@
     <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"କୀବୋର୍ଡ ସେଟିଂ"</string>
     <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"ସର୍ଟକଟ ସେଟ କରନ୍ତୁ"</string>
     <string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"କାଢ଼ି ଦିଅନ୍ତୁ"</string>
-    <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
-    <skip />
+    <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"ହଁ, ରିସେଟ କରନ୍ତୁ"</string>
     <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"ବାତିଲ କରନ୍ତୁ"</string>
     <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"କୀ ଦବାନ୍ତୁ"</string>
     <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"କୀ କମ୍ବିନେସନ ପୂର୍ବରୁ ବ୍ୟବହାର କରାଯାଉଛି। ଅନ୍ୟ ଏକ କୀ ବ୍ୟବହାର କରି ଦେଖନ୍ତୁ।"</string>
@@ -1486,8 +1482,7 @@
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"ଆପଣଙ୍କର କୀବୋର୍ଡରେ ଆକ୍ସନ କୀ\'କୁ ଦବାନ୍ତୁ"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"ବହୁତ ବଢ଼ିଆ!"</string>
     <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"ଆପଣ ସମସ୍ତ ଆପ୍ସ ଜେଶ୍ଚରକୁ ଭ୍ୟୁ କରିବା ସମ୍ପୂର୍ଣ୍ଣ କରିଛନ୍ତି"</string>
-    <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
-    <skip />
+    <string name="tutorial_animation_content_description" msgid="2698816574982370184">"ଟ୍ୟୁଟୋରିଆଲ ଆନିମେସନ, ପ୍ଲେ କରିବା ବିରତ କରି ପୁଣି ଆରମ୍ଭ କରିବାକୁ କ୍ଲିକ କରନ୍ତୁ।"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"କୀବୋର୍ଡ ବେକଲାଇଟ"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$dରୁ %1$d ନମ୍ବର ଲେଭେଲ"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"ହୋମ କଣ୍ଟ୍ରୋଲ୍ସ"</string>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index ba51bf2..68cd2b4 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -529,9 +529,9 @@
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"ਵਿਜੇਟ ਦੀ ਵਰਤੋਂ ਕਰ ਕੇ ਐਪ ਖੋਲ੍ਹਣ ਲਈ, ਤੁਹਾਨੂੰ ਇਹ ਪੁਸ਼ਟੀ ਕਰਨ ਦੀ ਲੋੜ ਪਵੇਗੀ ਕਿ ਇਹ ਤੁਸੀਂ ਹੀ ਹੋ। ਨਾਲ ਹੀ, ਇਹ ਵੀ ਧਿਆਨ ਵਿੱਚ ਰੱਖੋ ਕਿ ਕੋਈ ਵੀ ਉਨ੍ਹਾਂ ਨੂੰ ਦੇਖ ਸਕਦਾ ਹੈ, ਭਾਵੇਂ ਤੁਹਾਡਾ ਟੈਬਲੈੱਟ ਲਾਕ ਹੋਵੇ। ਹੋ ਸਕਦਾ ਹੈ ਕਿ ਕੁਝ ਵਿਜੇਟ ਤੁਹਾਡੀ ਲਾਕ ਸਕ੍ਰੀਨ ਲਈ ਨਾ ਬਣੇ ਹੋਣ ਅਤੇ ਉਨ੍ਹਾਂ ਨੂੰ ਇੱਥੇ ਸ਼ਾਮਲ ਕਰਨਾ ਅਸੁਰੱਖਿਅਤ ਹੋਵੇ।"</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"ਸਮਝ ਲਿਆ"</string>
     <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"ਵਿਜੇਟ"</string>
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
-    <skip />
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
+    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"\"ਵਿਜੇਟ\" ਸ਼ਾਰਟਕੱਟ ਨੂੰ ਸ਼ਾਮਲ ਕਰਨ ਲਈ, ਪੱਕਾ ਕਰੋ ਕਿ ਸੈਟਿੰਗਾਂ ਵਿੱਚ \"ਲਾਕ ਸਕ੍ਰੀਨ \'ਤੇ ਵਿਜੇਟ ਦਿਖਾਓ\" ਚਾਲੂ ਹੈ।"</string>
+    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"ਸੈਟਿੰਗਾਂ"</string>
+    <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
     <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ਵਰਤੋਂਕਾਰ ਸਵਿੱਚ ਕਰੋ"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ਪੁੱਲਡਾਊਨ ਮੀਨੂ"</string>
@@ -593,8 +593,7 @@
     <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>
-    <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
-    <skip />
+    <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"ਸੂਚਨਾ ਸੈਟਿੰਗਾਂ ਖੋਲ੍ਹੋ"</string>
     <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"\'ਪਰੇਸ਼ਾਨ ਨਾ ਕਰੋ\' ਵੱਲੋਂ ਸੂਚਨਾਵਾਂ ਨੂੰ ਰੋਕਿਆ ਗਿਆ"</string>
     <string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{ਕੋਈ ਸੂਚਨਾ ਨਹੀਂ ਹੈ}=1{{mode} ਵੱਲੋਂ ਸੂਚਨਾਵਾਂ ਨੂੰ ਬੰਦ ਕੀਤਾ ਗਿਆ}=2{{mode} ਅਤੇ ਇੱਕ ਹੋਰ ਮੋਡ ਵੱਲੋਂ ਸੂਚਨਾਵਾਂ ਨੂੰ ਬੰਦ ਕੀਤਾ ਗਿਆ}other{{mode} ਅਤੇ # ਹੋਰ ਮੋਡਾਂ ਵੱਲੋਂ ਸੂਚਨਾਵਾਂ ਨੂੰ ਬੰਦ ਕੀਤਾ ਗਿਆ}}"</string>
     <string name="media_projection_action_text" msgid="3634906766918186440">"ਹੁਣੇ ਸ਼ੁਰੂ ਕਰੋ"</string>
@@ -709,8 +708,7 @@
     <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"ਹੈੱਡ ਟਰੈਕਿੰਗ"</string>
     <string name="volume_ringer_change" msgid="3574969197796055532">"ਰਿੰਗਰ ਮੋਡ ਨੂੰ ਬਦਲਣ ਲਈ ਟੈਪ ਕਰੋ"</string>
     <string name="volume_ringer_mode" msgid="6867838048430807128">"ਰਿੰਗਰ ਮੋਡ"</string>
-    <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
-    <skip />
+    <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, ਰਿੰਗਰ ਮੋਡ ਨੂੰ ਬਦਲਣ ਲਈ ਟੈਪ ਕਰੋ"</string>
     <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"ਮਿਊਟ ਕਰੋ"</string>
     <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ਅਣਮਿਊਟ ਕਰੋ"</string>
     <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"ਥਰਥਰਾਹਟ"</string>
@@ -875,9 +873,12 @@
     <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_full_screen" msgid="336048080383640562">"ਸਪਲਿਟ ਸਕ੍ਰੀਨ ਤੋਂ ਪੂਰੀ ਸਕ੍ਰੀਨ \'ਤੇ ਸਵਿੱਚ ਕਰੋ"</string>
+    <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+    <skip />
+    <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+    <skip />
+    <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+    <skip />
     <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>
@@ -1430,20 +1431,17 @@
     <string name="shortcut_helper_title" msgid="8567500639300970049">"ਕੀ-ਬੋਰਡ ਸ਼ਾਰਟਕੱਟ"</string>
     <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"ਕੀ-ਬੋਰਡ ਸ਼ਾਰਟਕੱਟ ਵਿਉਂਤਬੱਧ ਕਰੋ"</string>
     <string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"ਕੀ ਸ਼ਾਰਟਕੱਟ ਨੂੰ ਹਟਾਉਣਾ ਹੈ?"</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"ਕੀ ਵਾਪਸ ਪੂਰਵ-ਨਿਰਧਾਰਿਤ \'ਤੇ ਰੀਸੈੱਟ ਕਰਨਾ ਹੈ?"</string>
     <string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"ਸ਼ਾਰਟਕੱਟ ਨਿਰਧਾਰਿਤ ਕਰਨ ਲਈ ਕੁੰਜੀ ਦਬਾਓ"</string>
     <string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"ਇਸ ਨਾਲ ਤੁਹਾਡੇ ਵਿਉਂਤੇ ਸ਼ਾਰਟਕੱਟ ਨੂੰ ਪੱਕੇ ਤੌਰ \'ਤੇ ਮਿਟਾ ਦਿੱਤਾ ਜਾਵੇਗਾ।"</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"ਇਸ ਨਾਲ ਤੁਹਾਡੇ ਸਾਰੇ ਵਿਉਂਤਬੱਧ ਸ਼ਾਰਟਕੱਟ ਪੱਕੇ ਤੌਰ \'ਤੇ ਮਿਟ ਜਾਣਗੇ।"</string>
     <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>
     <string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"ਕਾਰਵਾਈ ਜਾਂ Meta ਕੁੰਜੀ ਪ੍ਰਤੀਕ"</string>
     <string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"ਜੋੜ-ਚਿੰਨ੍ਹ ਦਾ ਪ੍ਰਤੀਕ"</string>
     <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"ਵਿਉਂਤਬੱਧ ਕਰੋ"</string>
-    <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
-    <skip />
+    <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"ਰੀਸੈੱਟ ਕਰੋ"</string>
     <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"ਹੋ ਗਿਆ"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ਪ੍ਰਤੀਕ ਦਾ ਵਿਸਤਾਰ ਕਰੋ"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ਜਾਂ"</string>
@@ -1453,8 +1451,7 @@
     <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"ਕੀ-ਬੋਰਡ ਸੈਟਿੰਗਾਂ"</string>
     <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"ਸ਼ਾਰਟਕੱਟ ਸੈੱਟ ਕਰੋ"</string>
     <string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"ਹਟਾਓ"</string>
-    <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
-    <skip />
+    <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"ਹਾਂ, ਰੀਸੈੱਟ ਕਰੋ"</string>
     <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"ਰੱਦ ਕਰੋ"</string>
     <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"ਕੁੰਜੀ ਦਬਾਓ"</string>
     <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"ਕੁੰਜੀ ਸੁਮੇਲ ਪਹਿਲਾਂ ਹੀ ਵਰਤੋਂ ਵਿੱਚ ਹੈ। ਕੋਈ ਹੋਰ ਕੁੰਜੀ ਵਰਤ ਕੇ ਦੇਖੋ।"</string>
@@ -1486,8 +1483,7 @@
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"ਆਪਣੇ ਕੀ-ਬੋਰਡ \'ਤੇ ਕਾਰਵਾਈ ਕੁੰਜੀ ਨੂੰ ਦਬਾਓ"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"ਬਹੁਤ ਵਧੀਆ!"</string>
     <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"ਤੁਸੀਂ \'ਸਾਰੀਆਂ ਐਪਾਂ ਦੇਖੋ\' ਦਾ ਇਸ਼ਾਰਾ ਪੂਰਾ ਕੀਤਾ ਹੈ"</string>
-    <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
-    <skip />
+    <string name="tutorial_animation_content_description" msgid="2698816574982370184">"ਟਿਊਟੋਰੀਅਲ ਐਨੀਮੇਸ਼ਨ, ਰੋਕਣ ਅਤੇ ਮੁੜ-ਚਾਲੂ ਕਰਨ ਲਈ ਕਲਿੱਕ ਕਰੋ।"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"ਕੀ-ਬੋਰਡ ਬੈਕਲਾਈਟ"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d ਵਿੱਚੋਂ %1$d ਪੱਧਰ"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"ਹੋਮ ਕੰਟਰੋਲ"</string>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 6785668..a970e28 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -116,7 +116,7 @@
     <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>
-    <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"Wybieranie aplikacji do nagrywania"</string>
+    <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"Wybierz aplikację do nagrywania"</string>
     <string name="screenrecord_audio_label" msgid="6183558856175159629">"Nagrywaj dźwięk"</string>
     <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Dźwięki z urządzenia"</string>
     <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Dźwięki odtwarzane na urządzeniu, na przykład muzyka, połączenia i dzwonki"</string>
@@ -531,6 +531,7 @@
     <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widżety"</string>
     <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Aby dodać skrót „Widżety”, upewnij się, że opcja „Pokaż widżety na ekranie blokady” jest włączona w ustawieniach."</string>
     <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Ustawienia"</string>
+    <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Przycisk Pokaż wygaszacz ekranu"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Przełącz użytkownika"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menu"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Wszystkie aplikacje i dane w tej sesji zostaną usunięte."</string>
@@ -591,8 +592,7 @@
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Powiadomienia"</string>
     <string name="notification_section_header_conversations" msgid="821834744538345661">"Rozmowy"</string>
     <string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Usuń wszystkie ciche powiadomienia"</string>
-    <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
-    <skip />
+    <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Otwórz ustawienia powiadomień"</string>
     <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Powiadomienia wstrzymane przez tryb Nie przeszkadzać"</string>
     <string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Brak powiadomień}=1{Powiadomienia są wstrzymane przez tryb {mode}}=2{Powiadomienia są wstrzymane przez tryb {mode} i 1 inny tryb}few{Powiadomienia są wstrzymane przez tryb {mode} i # inne tryby}many{Powiadomienia są wstrzymane przez tryb {mode} i # innych trybów}other{Powiadomienia są wstrzymane przez tryb {mode} i # innego trybu}}"</string>
     <string name="media_projection_action_text" msgid="3634906766918186440">"Rozpocznij teraz"</string>
@@ -707,8 +707,7 @@
     <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Śledzenie głowy"</string>
     <string name="volume_ringer_change" msgid="3574969197796055532">"Kliknij, aby zmienić tryb dzwonka"</string>
     <string name="volume_ringer_mode" msgid="6867838048430807128">"tryb dzwonka"</string>
-    <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
-    <skip />
+    <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>. Kliknij, aby zmienić tryb dzwonka"</string>
     <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"wycisz"</string>
     <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"wyłącz wyciszenie"</string>
     <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"włącz wibracje"</string>
@@ -873,9 +872,12 @@
     <string name="group_system_lock_screen" msgid="7391191300363416543">"Zablokuj ekran"</string>
     <string name="group_system_quick_memo" msgid="3764560265935722903">"Zanotuj"</string>
     <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Wielozadaniowość"</string>
-    <string name="system_multitasking_rhs" msgid="8714224917276297810">"Podziel ekran z bieżącą aplikacją widoczną po prawej"</string>
-    <string name="system_multitasking_lhs" msgid="8402954791206308783">"Podziel ekran z bieżącą aplikacją widoczną po lewej"</string>
-    <string name="system_multitasking_full_screen" msgid="336048080383640562">"Przełącz podzielony ekran na pełny ekran"</string>
+    <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+    <skip />
+    <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+    <skip />
+    <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+    <skip />
     <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Przełącz się na aplikację po prawej lub poniżej na podzielonym ekranie"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Przełącz się na aplikację po lewej lub powyżej na podzielonym ekranie"</string>
     <string name="system_multitasking_replace" msgid="7410071959803642125">"Podczas podzielonego ekranu: zastępowanie aplikacji"</string>
@@ -1438,8 +1440,7 @@
     <string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Ikona klawisza działania/meta"</string>
     <string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Ikona plusa"</string>
     <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Dostosuj"</string>
-    <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
-    <skip />
+    <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Resetuj"</string>
     <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Gotowe"</string>
     <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>
@@ -1481,8 +1482,7 @@
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Naciśnij klawisz działania na klawiaturze"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Brawo!"</string>
     <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Znasz już gest wyświetlania wszystkich aplikacji"</string>
-    <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
-    <skip />
+    <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Animacja z samouczkiem. Kliknij, aby wstrzymać lub wznowić odtwarzanie."</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Podświetlenie klawiatury"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Poziom %1$d z %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Sterowanie domem"</string>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index 9515227..f28de4e 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -334,7 +334,7 @@
     <string name="quick_settings_camera_label" msgid="5612076679385269339">"Acesso à câmera"</string>
     <string name="quick_settings_mic_label" msgid="8392773746295266375">"Acesso ao microfone"</string>
     <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Disponível"</string>
-    <string name="quick_settings_camera_mic_blocked" msgid="4710884905006788281">"Bloqueado"</string>
+    <string name="quick_settings_camera_mic_blocked" msgid="4710884905006788281">"Bloqueada"</string>
     <string name="quick_settings_media_device_label" msgid="8034019242363789941">"Dispositivo de mídia"</string>
     <string name="quick_settings_user_title" msgid="8673045967216204537">"Usuário"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
@@ -529,9 +529,9 @@
     <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>
     <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgets"</string>
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
-    <skip />
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
+    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Para adicionar o atalho Widgets, verifique se a opção \"Mostrar widgets na tela de bloqueio\" está ativada nas configurações."</string>
+    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Configurações"</string>
+    <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
     <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Trocar usuário"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menu suspenso"</string>
@@ -593,8 +593,7 @@
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Notificações"</string>
     <string name="notification_section_header_conversations" msgid="821834744538345661">"Conversas"</string>
     <string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Apagar todas as notificações silenciosas"</string>
-    <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
-    <skip />
+    <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Abrir configurações de notificações"</string>
     <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Notificações pausadas pelo modo \"Não perturbe\""</string>
     <string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Sem notificações}=1{Notificações pausadas pelo modo {mode}}=2{Notificações pausadas por {mode} e mais um modo}one{Notificações pausadas por {mode} e mais # modo}many{Notificações pausadas por {mode} e mais # de modos}other{Notificações pausadas por {mode} e mais # modos}}"</string>
     <string name="media_projection_action_text" msgid="3634906766918186440">"Iniciar agora"</string>
@@ -709,8 +708,7 @@
     <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Rastreamento de cabeça"</string>
     <string name="volume_ringer_change" msgid="3574969197796055532">"Toque para mudar o modo da campainha"</string>
     <string name="volume_ringer_mode" msgid="6867838048430807128">"modo da campainha"</string>
-    <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
-    <skip />
+    <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>. Toque para mudar o modo da campainha"</string>
     <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"desativar o som"</string>
     <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ativar o som"</string>
     <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrar"</string>
@@ -875,9 +873,12 @@
     <string name="group_system_lock_screen" msgid="7391191300363416543">"Tela de bloqueio"</string>
     <string name="group_system_quick_memo" msgid="3764560265935722903">"Criar nota"</string>
     <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Multitarefas"</string>
-    <string name="system_multitasking_rhs" msgid="8714224917276297810">"Usar a tela dividida com o app à direita"</string>
-    <string name="system_multitasking_lhs" msgid="8402954791206308783">"Usar a tela dividida com o app à esquerda"</string>
-    <string name="system_multitasking_full_screen" msgid="336048080383640562">"Mudar da tela dividida para a tela cheia"</string>
+    <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+    <skip />
+    <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+    <skip />
+    <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+    <skip />
     <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Mudar para o app à direita ou abaixo ao usar a tela dividida"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Mudar para o app à esquerda ou acima ao usar a tela dividida"</string>
     <string name="system_multitasking_replace" msgid="7410071959803642125">"Com a tela dividida: substituir um app por outro"</string>
@@ -1430,12 +1431,10 @@
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Atalhos do teclado"</string>
     <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Personalizar atalhos de teclado"</string>
     <string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Remover atalho?"</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Redefinir para o padrão?"</string>
     <string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Pressione a tecla para atribuir o atalho"</string>
     <string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Essa ação vai excluir permanentemente seu atalho personalizado."</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Essa ação vai excluir permanentemente todos os seus atalhos personalizados."</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Pesquisar atalhos"</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>
@@ -1452,8 +1451,7 @@
     <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Configurações do teclado"</string>
     <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Definir atalho"</string>
     <string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Remover"</string>
-    <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
-    <skip />
+    <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Sim, redefinir"</string>
     <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Cancelar"</string>
     <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Pressione a tecla"</string>
     <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Essa combinação de teclas já está em uso. Tente outra tecla."</string>
@@ -1485,8 +1483,7 @@
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Pressione a tecla de ação no teclado"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Muito bem!"</string>
     <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Você concluiu o gesto para ver todos os apps"</string>
-    <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
-    <skip />
+    <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Animação do tutorial. Clique para pausar ou retomar a reprodução."</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Luz de fundo do teclado"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Nível %1$d de %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Automação residencial"</string>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 40df4c0..83aa9cf3 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -531,6 +531,7 @@
     <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgets"</string>
     <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Para adicionar o atalho \"Widgets\", certifique-se de que a opção \"Mostrar widgets no ecrã de bloqueio\" está ativada nas definições."</string>
     <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Definições"</string>
+    <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Botão Mostrar proteção de ecrã"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Mudar utilizador"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menu pendente"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Todas as apps e dados desta sessão serão eliminados."</string>
@@ -591,8 +592,7 @@
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Notificações"</string>
     <string name="notification_section_header_conversations" msgid="821834744538345661">"Conversas"</string>
     <string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Limpar todas as notificações silenciosas"</string>
-    <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
-    <skip />
+    <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Abrir definições de notificações"</string>
     <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Notificações colocadas em pausa pelo modo Não incomodar."</string>
     <string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Sem notificações}=1{Notificações pausadas pelo modo {mode}}=2{Notificações pausadas pelo modo {mode} e mais um modo}many{Notificações pausadas pelo modo {mode} e mais # modos}other{Notificações pausadas pelo modo {mode} e mais # modos}}"</string>
     <string name="media_projection_action_text" msgid="3634906766918186440">"Começar agora"</string>
@@ -872,9 +872,12 @@
     <string name="group_system_lock_screen" msgid="7391191300363416543">"Ecrã de bloqueio"</string>
     <string name="group_system_quick_memo" msgid="3764560265935722903">"Tire notas"</string>
     <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Execução de várias tarefas em simultâneo"</string>
-    <string name="system_multitasking_rhs" msgid="8714224917276297810">"Use o ecrã dividido com a app atual à direita"</string>
-    <string name="system_multitasking_lhs" msgid="8402954791206308783">"Use o ecrã dividido com a app atual à esquerda"</string>
-    <string name="system_multitasking_full_screen" msgid="336048080383640562">"Mudar de ecrã dividido para ecrã inteiro"</string>
+    <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+    <skip />
+    <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+    <skip />
+    <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+    <skip />
     <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Mudar para a app à direita ou abaixo enquanto usa o ecrã dividido"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Mude para a app à esquerda ou acima enquanto usa o ecrã dividido"</string>
     <string name="system_multitasking_replace" msgid="7410071959803642125">"Durante o ecrã dividido: substituir uma app por outra"</string>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 9515227..f28de4e 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -334,7 +334,7 @@
     <string name="quick_settings_camera_label" msgid="5612076679385269339">"Acesso à câmera"</string>
     <string name="quick_settings_mic_label" msgid="8392773746295266375">"Acesso ao microfone"</string>
     <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Disponível"</string>
-    <string name="quick_settings_camera_mic_blocked" msgid="4710884905006788281">"Bloqueado"</string>
+    <string name="quick_settings_camera_mic_blocked" msgid="4710884905006788281">"Bloqueada"</string>
     <string name="quick_settings_media_device_label" msgid="8034019242363789941">"Dispositivo de mídia"</string>
     <string name="quick_settings_user_title" msgid="8673045967216204537">"Usuário"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
@@ -529,9 +529,9 @@
     <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>
     <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgets"</string>
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
-    <skip />
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
+    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Para adicionar o atalho Widgets, verifique se a opção \"Mostrar widgets na tela de bloqueio\" está ativada nas configurações."</string>
+    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Configurações"</string>
+    <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
     <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Trocar usuário"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menu suspenso"</string>
@@ -593,8 +593,7 @@
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Notificações"</string>
     <string name="notification_section_header_conversations" msgid="821834744538345661">"Conversas"</string>
     <string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Apagar todas as notificações silenciosas"</string>
-    <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
-    <skip />
+    <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Abrir configurações de notificações"</string>
     <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Notificações pausadas pelo modo \"Não perturbe\""</string>
     <string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Sem notificações}=1{Notificações pausadas pelo modo {mode}}=2{Notificações pausadas por {mode} e mais um modo}one{Notificações pausadas por {mode} e mais # modo}many{Notificações pausadas por {mode} e mais # de modos}other{Notificações pausadas por {mode} e mais # modos}}"</string>
     <string name="media_projection_action_text" msgid="3634906766918186440">"Iniciar agora"</string>
@@ -709,8 +708,7 @@
     <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Rastreamento de cabeça"</string>
     <string name="volume_ringer_change" msgid="3574969197796055532">"Toque para mudar o modo da campainha"</string>
     <string name="volume_ringer_mode" msgid="6867838048430807128">"modo da campainha"</string>
-    <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
-    <skip />
+    <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>. Toque para mudar o modo da campainha"</string>
     <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"desativar o som"</string>
     <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ativar o som"</string>
     <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrar"</string>
@@ -875,9 +873,12 @@
     <string name="group_system_lock_screen" msgid="7391191300363416543">"Tela de bloqueio"</string>
     <string name="group_system_quick_memo" msgid="3764560265935722903">"Criar nota"</string>
     <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Multitarefas"</string>
-    <string name="system_multitasking_rhs" msgid="8714224917276297810">"Usar a tela dividida com o app à direita"</string>
-    <string name="system_multitasking_lhs" msgid="8402954791206308783">"Usar a tela dividida com o app à esquerda"</string>
-    <string name="system_multitasking_full_screen" msgid="336048080383640562">"Mudar da tela dividida para a tela cheia"</string>
+    <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+    <skip />
+    <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+    <skip />
+    <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+    <skip />
     <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Mudar para o app à direita ou abaixo ao usar a tela dividida"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Mudar para o app à esquerda ou acima ao usar a tela dividida"</string>
     <string name="system_multitasking_replace" msgid="7410071959803642125">"Com a tela dividida: substituir um app por outro"</string>
@@ -1430,12 +1431,10 @@
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Atalhos do teclado"</string>
     <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Personalizar atalhos de teclado"</string>
     <string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Remover atalho?"</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Redefinir para o padrão?"</string>
     <string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Pressione a tecla para atribuir o atalho"</string>
     <string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Essa ação vai excluir permanentemente seu atalho personalizado."</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Essa ação vai excluir permanentemente todos os seus atalhos personalizados."</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Pesquisar atalhos"</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>
@@ -1452,8 +1451,7 @@
     <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Configurações do teclado"</string>
     <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Definir atalho"</string>
     <string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Remover"</string>
-    <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
-    <skip />
+    <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Sim, redefinir"</string>
     <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Cancelar"</string>
     <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Pressione a tecla"</string>
     <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Essa combinação de teclas já está em uso. Tente outra tecla."</string>
@@ -1485,8 +1483,7 @@
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Pressione a tecla de ação no teclado"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Muito bem!"</string>
     <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Você concluiu o gesto para ver todos os apps"</string>
-    <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
-    <skip />
+    <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Animação do tutorial. Clique para pausar ou retomar a reprodução."</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Luz de fundo do teclado"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Nível %1$d de %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Automação residencial"</string>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 3b5cd89..ea320a1 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -529,9 +529,9 @@
     <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>
     <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgeturi"</string>
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
-    <skip />
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
+    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Pentru a adăuga comanda rapidă Widgeturi, verifică dacă opțiunea Afișează widgeturi pe ecranul de blocare este activată în setări."</string>
+    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Setări"</string>
+    <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
     <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Schimbă utilizatorul"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"meniu vertical"</string>
@@ -593,8 +593,7 @@
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Notificări"</string>
     <string name="notification_section_header_conversations" msgid="821834744538345661">"Conversații"</string>
     <string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Șterge toate notificările silențioase"</string>
-    <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
-    <skip />
+    <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Deschide setările pentru notificări"</string>
     <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Notificări întrerupte prin „Nu deranja”"</string>
     <string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Nicio notificare}=1{Notificările au fost întrerupte de {mode}}=2{Notificările au fost întrerupte de {mode} și de un alt mod}few{Notificările au fost întrerupte de {mode} și de alte # moduri}other{Notificările au fost întrerupte de {mode} și de alte # de moduri}}"</string>
     <string name="media_projection_action_text" msgid="3634906766918186440">"Începe acum"</string>
@@ -709,8 +708,7 @@
     <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Urmărirea mișcărilor capului"</string>
     <string name="volume_ringer_change" msgid="3574969197796055532">"Atinge pentru a schimba modul soneriei"</string>
     <string name="volume_ringer_mode" msgid="6867838048430807128">"modul sonerie"</string>
-    <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
-    <skip />
+    <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, atinge pentru a schimba modul soneriei"</string>
     <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"dezactivează sunetul"</string>
     <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"activează sunetul"</string>
     <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrații"</string>
@@ -875,9 +873,12 @@
     <string name="group_system_lock_screen" msgid="7391191300363416543">"Ecranul de blocare"</string>
     <string name="group_system_quick_memo" msgid="3764560265935722903">"Creează o notă"</string>
     <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Multitasking"</string>
-    <string name="system_multitasking_rhs" msgid="8714224917276297810">"Folosește ecranul împărțit cu aplicația curentă în dreapta"</string>
-    <string name="system_multitasking_lhs" msgid="8402954791206308783">"Folosește ecranul împărțit cu aplicația curentă în stânga"</string>
-    <string name="system_multitasking_full_screen" msgid="336048080383640562">"Comută de la ecranul împărțit la ecranul complet"</string>
+    <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+    <skip />
+    <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+    <skip />
+    <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+    <skip />
     <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Treci la aplicația din dreapta sau de mai jos cu ecranul împărțit"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Treci la aplicația din stânga sau de mai sus cu ecranul împărțit"</string>
     <string name="system_multitasking_replace" msgid="7410071959803642125">"În modul ecran împărțit: înlocuiește o aplicație cu alta"</string>
@@ -1430,20 +1431,17 @@
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Comenzi rapide de la tastatură"</string>
     <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Personalizează comenzile rapide de la tastatură"</string>
     <string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Elimini comanda rapidă?"</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Resetezi la valorile prestabilite?"</string>
     <string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Apasă tasta pentru a atribui comanda rapidă"</string>
     <string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Astfel, se va șterge definitiv comanda rapidă personalizată."</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Astfel, se vor șterge definitiv toate comenzile rapide personalizate."</string>
     <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>
     <string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Pictograma pentru acțiune sau tastă Meta"</string>
     <string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Pictograma plus"</string>
     <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Personalizează"</string>
-    <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
-    <skip />
+    <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Resetează"</string>
     <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Gata"</string>
     <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>
@@ -1453,8 +1451,7 @@
     <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Setările tastaturii"</string>
     <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Setează o comandă rapidă"</string>
     <string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Elimină"</string>
-    <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
-    <skip />
+    <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Da, resetează"</string>
     <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Anulează"</string>
     <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Apasă tasta"</string>
     <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Combinația de taste este deja folosită. Încearcă altă tastă."</string>
@@ -1486,8 +1483,7 @@
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Apasă tasta de acțiuni de pe tastatură"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Felicitări!"</string>
     <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Ai finalizat gestul pentru afișarea tuturor aplicațiilor"</string>
-    <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
-    <skip />
+    <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Tutorial animat, dă clic pentru a întrerupe și a relua redarea."</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Iluminarea din spate a tastaturii"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Nivelul %1$d din %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Comenzi pentru locuință"</string>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index b026787..f785936 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -531,6 +531,8 @@
     <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Виджеты"</string>
     <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Чтобы создать ярлык \"Виджеты\", убедитесь, что в настройках включена функция \"Показывать виджеты на заблокированном экране\"."</string>
     <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Настройки"</string>
+    <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
+    <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Сменить пользователя."</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"раскрывающееся меню"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Все приложения и данные этого профиля будут удалены."</string>
@@ -591,8 +593,7 @@
     <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>
-    <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
-    <skip />
+    <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Открыть настройки уведомлений"</string>
     <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"В режиме \"Не беспокоить\" уведомления заблокированы"</string>
     <string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Уведомлений нет}=1{Режим \"{mode}\" приостанавливает уведомления}=2{Режим \"{mode}\" и ещё один режим приостанавливают уведомления}one{Режим \"{mode}\" и ещё # режим приостанавливают уведомления}few{Режим \"{mode}\" и ещё # режима приостанавливают уведомления}many{Режим \"{mode}\" и ещё # режимов приостанавливают уведомления}other{Режим \"{mode}\" и ещё # режима приостанавливают уведомления}}"</string>
     <string name="media_projection_action_text" msgid="3634906766918186440">"Начать"</string>
@@ -707,8 +708,7 @@
     <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Динамичное"</string>
     <string name="volume_ringer_change" msgid="3574969197796055532">"Нажмите, чтобы изменить режим звонка."</string>
     <string name="volume_ringer_mode" msgid="6867838048430807128">"режим звонка"</string>
-    <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
-    <skip />
+    <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>. Нажмите, чтобы изменить режим звонка."</string>
     <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"отключить звук"</string>
     <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"включить звук"</string>
     <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"включить вибрацию"</string>
@@ -873,9 +873,12 @@
     <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_full_screen" msgid="336048080383640562">"Изменить режим разделения экрана на полноэкранный режим"</string>
+    <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+    <skip />
+    <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+    <skip />
+    <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+    <skip />
     <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>
@@ -1438,8 +1441,7 @@
     <string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Значок клавиши Meta для выполнения действия"</string>
     <string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Значок плюса"</string>
     <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Настроить"</string>
-    <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
-    <skip />
+    <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Сбросить"</string>
     <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Готово"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Значок \"Развернуть\""</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"или"</string>
@@ -1481,8 +1483,7 @@
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Нажмите клавишу действия на клавиатуре."</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Блестяще!"</string>
     <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Вы выполнили жест для просмотра всех приложений."</string>
-    <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
-    <skip />
+    <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Анимация в руководстве. Нажмите, чтобы приостановить или продолжить воспроизведение."</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Подсветка клавиатуры"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Уровень %1$d из %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Управление домом"</string>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index a496c7c..68cdef7 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -529,9 +529,9 @@
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"විජට් එකක් භාවිතයෙන් යෙදුමක් විවෘත කිරීමට, ඔබට ඒ ඔබ බව සත්‍යාපනය කිරීමට අවශ්‍ය වනු ඇත. එසේම, ඔබේ ටැබ්ලටය අගුළු දමා ඇති විට පවා ඕනෑම කෙනෙකුට ඒවා බැලිය හැකි බව මතක තබා ගන්න. සමහර විජට් ඔබේ අගුළු තිරය සඳහා අදහස් කර නොතිබිය හැකි අතර මෙහි එක් කිරීමට අනාරක්ෂිත විය හැක."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"තේරුණා"</string>
     <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"විජට්"</string>
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
-    <skip />
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
+    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"\"විජට්\" කෙටිමඟ එක් කිරීමට, සැකසීම් තුළ \"අගුළු තිරයෙහි විජට් පෙන්වන්න\" සබල කර ඇති බවට වග බලා ගන්න."</string>
+    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"සැකසීම්"</string>
+    <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
     <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"පරිශීලක මාරුව"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"නිපතන මෙනුව"</string>
@@ -593,8 +593,7 @@
     <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>
-    <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
-    <skip />
+    <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"දැනුම්දීම් සැකසීම් විවෘත කරන්න"</string>
     <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"බාධා නොකරන්න මගින් විරාම කරන ලද දැනුම්දීම්"</string>
     <string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{දැනුම්දීම් නැත}=1{{mode} මගින් දැනුම්දීම් විරාම කරන ලදි}=2{{mode} සහ තව එක ප්‍රකාරයක් මගින් දැනුම්දීම් විරාම කරන ලදි}one{{mode} සහ තව ප්‍රකාර #ක් මගින් දැනුම්දීම් විරාම කරන ලදි}other{{mode} සහ තව ප්‍රකාර #ක් මගින් දැනුම්දීම් විරාම කරන ලදි}}"</string>
     <string name="media_projection_action_text" msgid="3634906766918186440">"දැන් අරඹන්න"</string>
@@ -709,8 +708,7 @@
     <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"හිස ලුහුබැඳීම"</string>
     <string name="volume_ringer_change" msgid="3574969197796055532">"නාදකය වෙනස් කිරීමට තට්ටු කරන්න"</string>
     <string name="volume_ringer_mode" msgid="6867838048430807128">"හඬ නඟන ආකාරය"</string>
-    <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
-    <skip />
+    <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, හඬ නඟන ප්‍රකාරය වෙනස් කිරීමට තට්ටු කරන්න"</string>
     <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"නිහඬ කරන්න"</string>
     <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"නිශ්ශබ්දතාවය ඉවත් කරන්න"</string>
     <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"කම්පනය"</string>
@@ -875,9 +873,12 @@
     <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_full_screen" msgid="336048080383640562">"බෙදුම් තිරයේ සිට පූර්ණ තිරයට මාරු වන්න"</string>
+    <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+    <skip />
+    <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+    <skip />
+    <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+    <skip />
     <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>
@@ -1430,20 +1431,17 @@
     <string name="shortcut_helper_title" msgid="8567500639300970049">"යතුරු පුවරු කෙටි මං"</string>
     <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"යතුරුපුවරු කෙටිමං අභිරුචිකරණය කරන්න"</string>
     <string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"කෙටිමඟ ඉවත් කරන්න ද?"</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"පෙරනිමියට යළි සකසන්න ද?"</string>
     <string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"කෙටිමඟ පැවරීමට යතුර ඔබන්න"</string>
     <string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"මෙය ඔබේ අභිරුචි කෙටිමඟ ස්ථිරවම මකනු ඇත."</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"මෙය ඔබේ සියලු අභිරුචි කෙටිමං ස්ථිරවම මකනු ඇත."</string>
     <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>
     <string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"ක්‍රියාව හෝ Meta යතුරු නිරූපකය"</string>
     <string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"ධන නිරූපකය"</string>
     <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"අභිරුචිකරණය කරන්න"</string>
-    <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
-    <skip />
+    <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"යළි සකසන්න"</string>
     <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"නිමයි"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"දිගහැරීම් නිරූපකය"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"හෝ"</string>
@@ -1453,8 +1451,7 @@
     <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"යතුරු පුවරු සැකසීම්"</string>
     <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"කෙටිමඟ සකසන්න"</string>
     <string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"ඉවත් කරන්න"</string>
-    <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
-    <skip />
+    <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"ඔව්, යළි සකසන්න"</string>
     <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"අවලංගු කරන්න"</string>
     <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"යතුර ඔබන්න"</string>
     <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"යතුරු සංයෝජනය දැනටමත් භාවිත වේ. වෙනත් යතුරක් උත්සාහ කරන්න."</string>
@@ -1486,8 +1483,7 @@
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"ඔබේ යතුරු පුවරුවේ ක්‍රියාකාරී යතුර ඔබන්න"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"හොඳින් කළා!"</string>
     <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"ඔබ සියලු යෙදුම් ඉංගිත බැලීම සම්පූර්ණ කර ඇත"</string>
-    <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
-    <skip />
+    <string name="tutorial_animation_content_description" msgid="2698816574982370184">"නිබන්ධන සජීවීකරණය, ක්‍රීඩාව විරාම කිරීමට සහ නැවත ආරම්භ කිරීමට ක්ලික් කරන්න."</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"යතුරු පුවරු පසු ආලෝකය"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$dන් %1$d වැනි මට්ටම"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"නිවෙස් පාලන"</string>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 0c4880d..552b2e3 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -116,7 +116,7 @@
     <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>
-    <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"Výber aplikácie na nahrávanie"</string>
+    <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"Vyberte aplikáciu, z ktorej chcete nahrávať"</string>
     <string name="screenrecord_audio_label" msgid="6183558856175159629">"Nahrávať zvuk"</string>
     <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Zvuk zariadenia"</string>
     <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Zvuk zo zariadenia, napríklad hudba, hovory a tóny zvonenia"</string>
@@ -531,6 +531,8 @@
     <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Miniaplikácie"</string>
     <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Ak chcete pridať odkaz Miniaplikácie, uistite sa, že v nastaveniach je zapnutá možnosť Zobrazovať miniaplikácie na uzamknutej obrazovke."</string>
     <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Nastavenia"</string>
+    <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
+    <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Prepnutie používateľa"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"rozbaľovacia ponuka"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Všetky aplikácie a údaje v tejto relácii budú odstránené."</string>
@@ -591,8 +593,7 @@
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Upozornenia"</string>
     <string name="notification_section_header_conversations" msgid="821834744538345661">"Konverzácie"</string>
     <string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Vymazať všetky tiché upozornenia"</string>
-    <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
-    <skip />
+    <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Otvoriť nastavenia upozornení"</string>
     <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Upozornenia sú pozastavené režimom bez vyrušení"</string>
     <string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Žiadne upozornenia}=1{Upozornenia boli pozastavené režimom {mode}}=2{Upozornenia boli pozastavené režimom {mode} a jedným ďalším}few{Upozornenia boli pozastavené režimom {mode} a # ďalšími}many{Notifications paused by {mode} and # other modes}other{Upozornenia boli pozastavené režimom {mode} a # ďalšími}}"</string>
     <string name="media_projection_action_text" msgid="3634906766918186440">"Spustiť"</string>
@@ -707,8 +708,7 @@
     <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Sledovanie hlavy"</string>
     <string name="volume_ringer_change" msgid="3574969197796055532">"Režim zvonenia zmeníte klepnutím"</string>
     <string name="volume_ringer_mode" msgid="6867838048430807128">"režim zvonenia"</string>
-    <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
-    <skip />
+    <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, režim zvonenia zmeníte klepnutím"</string>
     <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"vypnite zvuk"</string>
     <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"zapnite zvuk"</string>
     <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"zapnite vibrovanie"</string>
@@ -873,9 +873,12 @@
     <string name="group_system_lock_screen" msgid="7391191300363416543">"Uzamknutie obrazovky"</string>
     <string name="group_system_quick_memo" msgid="3764560265935722903">"Napísanie poznámky"</string>
     <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Multitasking"</string>
-    <string name="system_multitasking_rhs" msgid="8714224917276297810">"Rozdeliť obrazovku, aktuálna aplikácia vpravo"</string>
-    <string name="system_multitasking_lhs" msgid="8402954791206308783">"Rozdeliť obrazovku, aktuálna aplikácia vľavo"</string>
-    <string name="system_multitasking_full_screen" msgid="336048080383640562">"Prepnutie rozdelenej obrazovky na celú"</string>
+    <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+    <skip />
+    <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+    <skip />
+    <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+    <skip />
     <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Prechod na aplikáciu vpravo alebo dole pri rozdelenej obrazovke"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Prechod na aplikáciu vľavo alebo hore pri rozdelenej obrazovke"</string>
     <string name="system_multitasking_replace" msgid="7410071959803642125">"Počas rozdelenej obrazovky: nahradenie aplikácie inou"</string>
@@ -1438,8 +1441,7 @@
     <string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Ikona akčného klávesa alebo metaklávesa"</string>
     <string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Ikona plus"</string>
     <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Prispôsobiť"</string>
-    <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
-    <skip />
+    <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Resetovať"</string>
     <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Hotovo"</string>
     <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>
@@ -1481,8 +1483,7 @@
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Stlačte na klávesnici akčný kláves"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Dobre!"</string>
     <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Použili ste gesto na zobrazenie všetkých aplikácií."</string>
-    <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
-    <skip />
+    <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Výuková animácia, kliknutím pozastavíte alebo obnovíte prehrávanie."</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Podsvietenie klávesnice"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"%1$d. úroveň z %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Ovládanie domácnosti"</string>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index dbeed9d..33d400c 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -531,6 +531,7 @@
     <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Pripomočki"</string>
     <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Če želite dodati bližnjico »Pripomočki«, v nastavitvah omogočite možnost »Prikaz pripomočkov na zaklenjenem zaslonu«."</string>
     <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Nastavitve"</string>
+    <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Pokaži gumb za ohranjevalnik zaslona"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Preklop med uporabniki"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"spustni meni"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Vse aplikacije in podatki v tej seji bodo izbrisani."</string>
@@ -591,8 +592,7 @@
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Obvestila"</string>
     <string name="notification_section_header_conversations" msgid="821834744538345661">"Pogovori"</string>
     <string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Brisanje vseh tihih obvestil"</string>
-    <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
-    <skip />
+    <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Odpiranje nastavitev obvestil"</string>
     <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Prikazovanje obvestil je začasno zaustavljeno z načinom »ne moti«"</string>
     <string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Ni obvestil}=1{Prikazovanje obvestil je začasno zaustavljeno z načinom {mode}}=2{Prikazovanje obvestil je začasno zaustavljeno z načinom {mode} in še enim drugim načinom}one{Prikazovanje obvestil je začasno zaustavljeno z načinom {mode} in še # drugim načinom}two{Prikazovanje obvestil je začasno zaustavljeno z načinom {mode} in še # drugima načinoma}few{Prikazovanje obvestil je začasno zaustavljeno z načinom {mode} in še # drugimi načini}other{Prikazovanje obvestil je začasno zaustavljeno z načinom {mode} in še # drugimi načini}}"</string>
     <string name="media_projection_action_text" msgid="3634906766918186440">"Začni zdaj"</string>
@@ -707,8 +707,7 @@
     <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Spremljanje glave"</string>
     <string name="volume_ringer_change" msgid="3574969197796055532">"Dotaknite se, če želite spremeniti način zvonjenja."</string>
     <string name="volume_ringer_mode" msgid="6867838048430807128">"način zvonjenja"</string>
-    <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
-    <skip />
+    <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, dotaknite se, če želite spremeniti način zvonjenja."</string>
     <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"izklop zvoka"</string>
     <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"vklop zvoka"</string>
     <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibriranje"</string>
@@ -873,9 +872,12 @@
     <string name="group_system_lock_screen" msgid="7391191300363416543">"Zaklepanje zaslona"</string>
     <string name="group_system_quick_memo" msgid="3764560265935722903">"Ustvarjanje zapiska"</string>
     <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Večopravilnost"</string>
-    <string name="system_multitasking_rhs" msgid="8714224917276297810">"Uporaba razdeljenega zaslona s trenutno aplikacijo na desni"</string>
-    <string name="system_multitasking_lhs" msgid="8402954791206308783">"Uporaba razdeljenega zaslona s trenutno aplikacijo na levi"</string>
-    <string name="system_multitasking_full_screen" msgid="336048080383640562">"Preklop iz razdeljenega zaslona v celozaslonski način"</string>
+    <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+    <skip />
+    <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+    <skip />
+    <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+    <skip />
     <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Preklop na aplikacijo desno ali spodaj med uporabo razdeljenega zaslona"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Preklop na aplikacijo levo ali zgoraj med uporabo razdeljenega zaslona"</string>
     <string name="system_multitasking_replace" msgid="7410071959803642125">"Pri razdeljenem zaslonu: medsebojna zamenjava aplikacij"</string>
@@ -1480,8 +1482,7 @@
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Pritisnite tipko za dejanja na tipkovnici"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Odlično!"</string>
     <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Izvedli ste potezo za ogled vseh aplikacij"</string>
-    <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
-    <skip />
+    <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Animacija v vadnici, kliknite za začasno zaustavitev in nadaljevanje predvajanja."</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Osvetlitev tipkovnice"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Stopnja %1$d od %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Kontrolniki za dom"</string>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index 743aad0..ce7c9c0 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -529,9 +529,9 @@
     <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>
     <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Miniaplikacionet"</string>
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
-    <skip />
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
+    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Për të shtuar shkurtoren e \"Miniaplikacioneve\", sigurohu që \"Shfaq miniaplikacionet në ekranin e kyçjes\" të jetë aktivizuar te cilësimet."</string>
+    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Cilësimet"</string>
+    <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
     <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Ndërro përdorues"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menyja me tërheqje poshtë"</string>
@@ -709,8 +709,7 @@
     <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Ndjekja e lëvizjeve të kokës"</string>
     <string name="volume_ringer_change" msgid="3574969197796055532">"Trokit për të ndryshuar modalitetin e ziles"</string>
     <string name="volume_ringer_mode" msgid="6867838048430807128">"modaliteti i ziles"</string>
-    <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
-    <skip />
+    <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>. Trokit për të ndryshuar modalitetin e ziles"</string>
     <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"çaktivizo audion"</string>
     <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"aktivizo audion"</string>
     <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"lësho dridhje"</string>
@@ -875,9 +874,12 @@
     <string name="group_system_lock_screen" msgid="7391191300363416543">"Ekrani i kyçjes"</string>
     <string name="group_system_quick_memo" msgid="3764560265935722903">"Mbaj një shënim"</string>
     <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Kryerja e shumë detyrave"</string>
-    <string name="system_multitasking_rhs" msgid="8714224917276297810">"Përdor ekranin e ndarë me aplikacionin aktual në të djathtë"</string>
-    <string name="system_multitasking_lhs" msgid="8402954791206308783">"Përdor ekranin e ndarë me aplikacionin aktual në të majtë"</string>
-    <string name="system_multitasking_full_screen" msgid="336048080383640562">"Kalo nga ekrani i ndarë në ekranin e plotë"</string>
+    <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+    <skip />
+    <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+    <skip />
+    <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+    <skip />
     <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Kalo tek aplikacioni djathtas ose poshtë kur përdor ekranin e ndarë"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Kalo tek aplikacioni në të majtë ose sipër kur përdor ekranin e ndarë"</string>
     <string name="system_multitasking_replace" msgid="7410071959803642125">"Gjatë ekranit të ndarë: zëvendëso një aplikacion me një tjetër"</string>
@@ -1430,20 +1432,17 @@
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Shkurtoret e tastierës"</string>
     <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Personalizo shkurtoret e tastierës"</string>
     <string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Të hiqet shkurtorja?"</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Të rivendosen përsëri te parazgjedhjet?"</string>
     <string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Shtyp tastin për të caktuar shkurtoren"</string>
     <string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Kjo do ta fshijë përgjithmonë shkurtoren tënde të personalizuar."</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Kjo do të fshijë përgjithmonë të gjitha shkurtoret e tua të personalizuara."</string>
     <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>
     <string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Ikona e tastit të veprimit ose tastit Meta"</string>
     <string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Ikona e plusit"</string>
     <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Personalizo"</string>
-    <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
-    <skip />
+    <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Rivendos"</string>
     <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"U krye"</string>
     <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>
@@ -1453,8 +1452,7 @@
     <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Cilësimet e tastierës"</string>
     <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Cakto shkurtoren"</string>
     <string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Hiq"</string>
-    <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
-    <skip />
+    <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Po, rivendosi"</string>
     <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Anulo"</string>
     <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Shtyp tastin"</string>
     <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Kombinimi i tasteve është tashmë në përdorim. Provo një tast tjetër."</string>
@@ -1486,8 +1484,7 @@
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Shtyp tastin e veprimit në tastierë"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Shumë mirë!"</string>
     <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Përfundove gjestin për shikimin e të gjitha aplikacioneve"</string>
-    <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
-    <skip />
+    <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Animacioni udhëzues. Kliko për të vendosur në pauzë dhe për të vazhduar luajtjen."</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Drita e sfondit e tastierës"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Niveli: %1$d nga %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Kontrollet e shtëpisë"</string>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index 9a15268..8ae5446 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -531,6 +531,7 @@
     <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Виџети"</string>
     <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Да бисте додали пречицу Виџети, уверите се да је у подешавањима омогућено Приказуј виџете на закључаном екрану."</string>
     <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Подешавања"</string>
+    <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Дугме Прикажи чувар екрана"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Замени корисника"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"падајући мени"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Све апликације и подаци у овој сесији ће бити избрисани."</string>
@@ -591,8 +592,7 @@
     <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>
-    <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
-    <skip />
+    <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Отворите подешавања обавештења"</string>
     <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Обавештења су паузирана режимом Не узнемиравај"</string>
     <string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Нема обавештења}=1{Обавештења је паузирао {mode}}=2{Обавештења су паузирали {mode} и још један режим}one{Обавештења су паузирали {mode} и још # режим}few{Обавештења су паузирали {mode} и још # режима}other{Обавештења су паузирали {mode} и још # режима}}"</string>
     <string name="media_projection_action_text" msgid="3634906766918186440">"Започни"</string>
@@ -707,8 +707,7 @@
     <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Праћење главе"</string>
     <string name="volume_ringer_change" msgid="3574969197796055532">"Додирните да бисте променили режим звона"</string>
     <string name="volume_ringer_mode" msgid="6867838048430807128">"режим звона"</string>
-    <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
-    <skip />
+    <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, додирните да бисте променили режим звона"</string>
     <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"искључите звук"</string>
     <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"укључите звук"</string>
     <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"вибрација"</string>
@@ -873,9 +872,12 @@
     <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_full_screen" msgid="336048080383640562">"Пређи са подељеног екрана на цео екран"</string>
+    <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+    <skip />
+    <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+    <skip />
+    <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+    <skip />
     <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>
@@ -1438,8 +1440,7 @@
     <string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Икона тастера за радњу или мета тастера"</string>
     <string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Икона знака плус"</string>
     <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Прилагоди"</string>
-    <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
-    <skip />
+    <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Ресетуј"</string>
     <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Готово"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Икона за проширивање"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"или"</string>
@@ -1481,8 +1482,7 @@
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Притисните тастер радњи на тастатури"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Одлично!"</string>
     <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Довршили сте покрет за приказивање свих апликација."</string>
-    <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
-    <skip />
+    <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Анимација водича, кликните да бисте паузирали и наставили репродукцију."</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Позадинско осветљење тастатуре"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"%1$d. ниво од %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Контроле за дом"</string>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index c8fee61..dcb1955b 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -529,9 +529,9 @@
     <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>
     <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgetar"</string>
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
-    <skip />
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
+    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Om du vill lägga till genvägen Widgetar måste du se till att Visa widgetar på låsskärmen är aktiverat i inställningarna."</string>
+    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Inställningar"</string>
+    <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
     <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Byt användare"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"rullgardinsmeny"</string>
@@ -709,8 +709,7 @@
     <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Huvudspårning"</string>
     <string name="volume_ringer_change" msgid="3574969197796055532">"Tryck för att ändra ringsignalens läge"</string>
     <string name="volume_ringer_mode" msgid="6867838048430807128">"ringsignalläge"</string>
-    <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
-    <skip />
+    <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>: Tryck för att ändra ringsignalens läge"</string>
     <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"stänga av ljudet"</string>
     <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"slå på ljudet"</string>
     <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibration"</string>
@@ -875,9 +874,12 @@
     <string name="group_system_lock_screen" msgid="7391191300363416543">"Lås skärmen"</string>
     <string name="group_system_quick_memo" msgid="3764560265935722903">"Anteckna"</string>
     <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Multikörning"</string>
-    <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>
+    <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+    <skip />
+    <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+    <skip />
+    <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+    <skip />
     <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>
@@ -1430,12 +1432,10 @@
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Kortkommandon"</string>
     <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Anpassa kortkommandon"</string>
     <string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Vill du ta bort kortkommandot?"</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Vill du återställa till standardinställningarna?"</string>
     <string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Tryck på tangenten för att tilldela ett kortkommando"</string>
     <string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Det anpassade kortkommandot raderas permanent."</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Alla anpassade genvägar raderas permanent."</string>
     <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>
@@ -1452,8 +1452,7 @@
     <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Tangentbordsinställningar"</string>
     <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Ange kortkommando"</string>
     <string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Ta bort"</string>
-    <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
-    <skip />
+    <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Ja, återställ"</string>
     <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Avbryt"</string>
     <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Tryck på tangenten"</string>
     <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Tangentkombinationen används redan. Testa en annan tangent."</string>
@@ -1485,8 +1484,7 @@
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Tryck på åtgärdstangenten på tangentbordet"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Bra gjort!"</string>
     <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Du är klar med rörelsen för att se alla apparna."</string>
-    <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
-    <skip />
+    <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Animation för guiden: Klicka för att pausa och återuppta uppspelningen."</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Bakgrundsbelysning för tangentbord"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Nivå %1$d av %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Hemstyrning"</string>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 44bac92..cd2c448 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -529,9 +529,9 @@
     <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>
     <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Wijeti"</string>
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
-    <skip />
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
+    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Ili uweke njia ya mkato ya \"Wijeti\", hakikisha kuwa kitufe cha \"Onyesha wijeti kwenye skrini iliyofungwa\" kimewashwa katika mipangilio."</string>
+    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Mipangilio"</string>
+    <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
     <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Badili mtumiaji"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menyu ya kuvuta chini"</string>
@@ -593,8 +593,7 @@
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Arifa"</string>
     <string name="notification_section_header_conversations" msgid="821834744538345661">"Mazungumzo"</string>
     <string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Futa arifa zote zisizo na sauti"</string>
-    <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
-    <skip />
+    <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Fungua mipangilio ya arifa"</string>
     <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Kipengele cha Usinisumbue kimesitisha arifa"</string>
     <string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Hakuna arifa}=1{Arifa zimesitishwa na {mode}}=2{Arifa zimesitishwa na {mode} na hali nyingine moja}other{Arifa zimesitishwa na {mode} na hali nyingine #}}"</string>
     <string name="media_projection_action_text" msgid="3634906766918186440">"Anza sasa"</string>
@@ -709,8 +708,7 @@
     <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Ufuatilizi wa Kichwa"</string>
     <string name="volume_ringer_change" msgid="3574969197796055532">"Gusa ili ubadilishe hali ya programu inayotoa milio ya simu"</string>
     <string name="volume_ringer_mode" msgid="6867838048430807128">"hali ya programu inayotoa milio ya simu"</string>
-    <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
-    <skip />
+    <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, gusa ili ubadilishe hali ya programu inayotoa milio ya simu"</string>
     <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"zima sauti"</string>
     <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"washa sauti"</string>
     <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"tetema"</string>
@@ -875,9 +873,12 @@
     <string name="group_system_lock_screen" msgid="7391191300363416543">"Funga skrini"</string>
     <string name="group_system_quick_memo" msgid="3764560265935722903">"Andika dokezo"</string>
     <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Majukumu mengi"</string>
-    <string name="system_multitasking_rhs" msgid="8714224917276297810">"Tumia hali ya kugawa skrini na programu ya sasa iwe upande wa kulia"</string>
-    <string name="system_multitasking_lhs" msgid="8402954791206308783">"Tumia hali ya kugawa skrini na programu ya sasa iwe upande wa kushoto"</string>
-    <string name="system_multitasking_full_screen" msgid="336048080383640562">"Badilisha kutoka skrini iliyogawanywa utumie skrini nzima"</string>
+    <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+    <skip />
+    <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+    <skip />
+    <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+    <skip />
     <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Badilisha ili uende kwenye programu iliyo kulia au chini unapotumia hali ya kugawa skrini"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Badilisha uende kwenye programu iliyo kushoto au juu unapotumia hali ya kugawa skrini"</string>
     <string name="system_multitasking_replace" msgid="7410071959803642125">"Ukigawanya skrini: badilisha kutoka programu moja hadi nyingine"</string>
@@ -1430,20 +1431,17 @@
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Mikato ya kibodi"</string>
     <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Weka mapendeleo ya mikato ya kibodi"</string>
     <string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Ungependa kuondoa njia ya mkato?"</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Ungependa kurejesha njia za mkato chaguomsingi?"</string>
     <string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Bonyeza kitufe ukabidhi njia ya mkato"</string>
     <string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Hatua hii itaondoa kabisa njia yako maalum ya mkato."</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Hatua hii itafuta kabisa njia zako zote maalum za mkato."</string>
     <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>
     <string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Aikoni ya kitufe cha Vitendo au cha Meta"</string>
     <string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Aikoni ya alama ya kujumlisha"</string>
     <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Weka mapendeleo"</string>
-    <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
-    <skip />
+    <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Weka upya"</string>
     <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Nimemaliza"</string>
     <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>
@@ -1453,8 +1451,7 @@
     <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Mipangilio ya Kibodi"</string>
     <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Weka njia ya mkato"</string>
     <string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Ondoa"</string>
-    <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
-    <skip />
+    <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Ndiyo, rejesha"</string>
     <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Acha"</string>
     <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Bonyeza kitufe"</string>
     <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Tayari unatumia mchanganyiko wa vitufe. Jaribu kitufe kingine."</string>
@@ -1486,8 +1483,7 @@
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Bonyeza kitufe cha vitendo kwenye kibodi yako"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Vizuri sana!"</string>
     <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Umekamilisha mafunzo ya mguso wa kuangalia programu zote"</string>
-    <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
-    <skip />
+    <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Uhuishaji wa mafunzo, bofya ili usitishe na uendelee kucheza."</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Mwanga chini ya kibodi"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Kiwango cha %1$d kati ya %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Dhibiti Vifaa Nyumbani"</string>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index 4c65095..ce630bb 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -529,9 +529,9 @@
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"விட்ஜெட்டைப் பயன்படுத்தி ஆப்ஸைத் திறக்க, அது நீங்கள்தான் என்பதை உறுதிசெய்ய வேண்டும். அத்துடன், உங்கள் டேப்லெட் பூட்டப்பட்டிருந்தாலும்கூட அவற்றை யார் வேண்டுமானாலும் பார்க்கலாம் என்பதை நினைவில்கொள்ளுங்கள். சில விட்ஜெட்கள் உங்கள் பூட்டுத் திரைக்காக உருவாக்கப்பட்டவை அல்ல என்பதையும் அவற்றை இங்கே சேர்ப்பது பாதுகாப்பற்றதாக இருக்கக்கூடும் என்பதையும் நினைவில்கொள்ளுங்கள்."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"சரி"</string>
     <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"விட்ஜெட்கள்"</string>
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
-    <skip />
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
+    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"“விட்ஜெட்கள்” ஷார்ட்கட்டைச் சேர்க்க, அமைப்புகளில் “பூட்டுத் திரையில் விட்ஜெட்களைக் காட்டுதல்” அமைப்பு இயக்கப்பட்டிருப்பதை உறுதிசெய்துகொள்ளுங்கள்."</string>
+    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"அமைப்புகள்"</string>
+    <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
     <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"பயனரை மாற்று"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"கீழ் இழுக்கும் மெனு"</string>
@@ -593,8 +593,7 @@
     <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>
-    <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
-    <skip />
+    <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"அறிவிப்பு அமைப்புகளைத் திறக்கும்"</string>
     <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"\'தொந்தரவு செய்ய வேண்டாம்\' அம்சத்தின் மூலம் அறிவிப்புகள் இடைநிறுத்தப்பட்டுள்ளன"</string>
     <string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{அறிவிப்புகள் இல்லை}=1{{mode} பயன்முறையால் அறிவிப்புகள் இடைநிறுத்தப்பட்டுள்ளன}=2{{mode} மற்றும் வேறொரு பயன்முறையால் அறிவிப்புகள் இடைநிறுத்தப்பட்டுள்ளன}other{{mode} மற்றும் வேறு # பயன்முறைகளால் அறிவிப்புகள் இடைநிறுத்தப்பட்டுள்ளன}}"</string>
     <string name="media_projection_action_text" msgid="3634906766918186440">"இப்போது தொடங்கு"</string>
@@ -709,8 +708,7 @@
     <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"ஹெட் டிராக்கிங்"</string>
     <string name="volume_ringer_change" msgid="3574969197796055532">"ரிங்கர் பயன்முறையை மாற்ற தட்டவும்"</string>
     <string name="volume_ringer_mode" msgid="6867838048430807128">"ரிங்கர் பயன்முறை"</string>
-    <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
-    <skip />
+    <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, ரிங்கர் பயன்முறையை மாற்ற தட்டலாம்"</string>
     <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"ஒலியடக்கும்"</string>
     <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ஒலி இயக்கும்"</string>
     <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"அதிர்வுறும்"</string>
@@ -875,9 +873,12 @@
     <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_full_screen" msgid="336048080383640562">"திரைப் பிரிப்பு பயன்முறையிலிருந்து முழுத்திரைக்கு மாற்றுதல்"</string>
+    <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+    <skip />
+    <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+    <skip />
+    <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+    <skip />
     <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>
@@ -1430,20 +1431,17 @@
     <string name="shortcut_helper_title" msgid="8567500639300970049">"கீபோர்டு ஷார்ட்கட்கள்"</string>
     <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"கீபோர்டு ஷார்ட்கட்களைப் பிரத்தியேகப்படுத்துதல்"</string>
     <string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"ஷார்ட்கட்டை அகற்றவா?"</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"மீண்டும் இயல்புநிலைக்கு மீட்டமைக்கவா?"</string>
     <string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"ஷார்ட்கட்டை அமைக்க பட்டனை அழுத்துங்கள்"</string>
     <string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"இது உங்கள் பிரத்தியேக ஷார்ட்கட்டை நிரந்தரமாக நீக்கும்."</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"இது உங்கள் பிரத்தியேக ஷார்ட்கட்கள் அனைத்தையும் நிரந்தரமாக நீக்கும்."</string>
     <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>
     <string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"ஆக்‌ஷன்/மெட்டா பட்டன் ஐகான்"</string>
     <string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"பிளஸ் ஐகான்"</string>
     <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"பிரத்தியேகப்படுத்தும்"</string>
-    <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
-    <skip />
+    <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"மீட்டமை"</string>
     <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"முடிந்தது"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"விரிவாக்குவதற்கான ஐகான்"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"அல்லது"</string>
@@ -1453,8 +1451,7 @@
     <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"கீபோர்டு அமைப்புகள்"</string>
     <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"ஷார்ட்கட்டை அமையுங்கள்"</string>
     <string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"அகற்று"</string>
-    <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
-    <skip />
+    <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"ஆம். மீட்டமை"</string>
     <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"ரத்துசெய்"</string>
     <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"பட்டனை அழுத்துங்கள்"</string>
     <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"பட்டன் சேர்க்கை ஏற்கெனவே பயன்பாட்டில் உள்ளது. வேறொரு பட்டனைப் பயன்படுத்திப் பார்க்கவும்."</string>
@@ -1486,8 +1483,7 @@
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"உங்கள் கீபோர்டில் ஆக்‌ஷன் பட்டனை அழுத்தவும்"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"அருமை!"</string>
     <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"அனைத்து ஆப்ஸையும் பார்ப்பதற்கான சைகை பயிற்சியை நிறைவுசெய்துவிட்டீர்கள்"</string>
-    <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
-    <skip />
+    <string name="tutorial_animation_content_description" msgid="2698816574982370184">"பயிற்சி அனிமேஷன், இடைநிறுத்தவும் மீண்டும் இயக்கவும் கிளிக் செய்யலாம்."</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"கீபோர்டு பேக்லைட்"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"நிலை, %2$d இல் %1$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"ஹோம் கன்ட்ரோல்கள்"</string>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index ebed1877..20d4cf7 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -531,6 +531,7 @@
     <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"విడ్జెట్‌లు"</string>
     <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"\"విడ్జెట్‌ల\" షార్ట్‌కట్‌ను జోడించడానికి, సెట్టింగ్‌లలో \"లాక్ స్క్రీన్‌లో విడ్జెట్‌లను చూపండి\" అనే ఆప్షన్‌ను ఎనేబుల్ చేసినట్లు నిర్ధారించుకోండి."</string>
     <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"సెట్టింగ్‌లు"</string>
+    <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"స్క్రీన్ సేవర్ బటన్‌ను చూపండి"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"వినియోగదారుని మార్చు"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"పుల్‌డౌన్ మెనూ"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ఈ సెషన్‌లోని అన్ని యాప్‌లు మరియు డేటా తొలగించబడతాయి."</string>
@@ -591,8 +592,7 @@
     <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>
-    <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
-    <skip />
+    <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"నోటిఫికేషన్‌ల సెట్టింగ్‌లను తెరవండి"</string>
     <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"అంతరాయం కలిగించవద్దు ద్వారా నోటిఫికేషన్‌లు పాజ్ చేయబడ్డాయి"</string>
     <string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{నోటిఫికేషన్‌లు ఏవీ లేవు}=1{{mode} ద్వారా నోటిఫికేషన్‌లు పాజ్ చేయబడ్డాయి}=2{నోటిఫికేషన్‌లు, {mode}, మరో ఒక మోడ్ ద్వారా పాజ్ చేయబడ్డాయి}other{నోటిఫికేషన్‌లు, {mode}, మరో # మోడ్‌ల ద్వారా పాజ్ చేయబడ్డాయి}}"</string>
     <string name="media_projection_action_text" msgid="3634906766918186440">"ఇప్పుడే ప్రారంభించండి"</string>
@@ -707,8 +707,7 @@
     <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"హెడ్ ట్రాకింగ్"</string>
     <string name="volume_ringer_change" msgid="3574969197796055532">"రింగర్ మోడ్‌ను మార్చడానికి ట్యాప్ చేయండి"</string>
     <string name="volume_ringer_mode" msgid="6867838048430807128">"రింగర్ మోడ్"</string>
-    <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
-    <skip />
+    <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, రింగర్ మోడ్‌ను మార్చడానికి ట్యాప్ చేయండి"</string>
     <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"మ్యూట్ చేయి"</string>
     <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"అన్‌మ్యూట్ చేయి"</string>
     <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"వైబ్రేట్"</string>
@@ -873,9 +872,12 @@
     <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_full_screen" msgid="336048080383640562">"స్ప్లిట్ స్క్రీన్‌ను ఫుల్ స్క్రీన్‌కు మార్చండి"</string>
+    <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+    <skip />
+    <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+    <skip />
+    <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+    <skip />
     <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>
@@ -1438,8 +1440,7 @@
     <string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"యాక్షన్ లేదా మెటా కీ చిహ్నం"</string>
     <string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"ప్లస్ చిహ్నం"</string>
     <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"అనుకూలంగా మార్చండి"</string>
-    <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
-    <skip />
+    <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"రీసెట్ చేయండి"</string>
     <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"పూర్తయింది"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"విస్తరించండి చిహ్నం"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"లేదా"</string>
@@ -1481,8 +1482,7 @@
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"మీ కీబోర్డ్‌లో యాక్షన్ కీని నొక్కండి"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"చక్కగా చేశారు!"</string>
     <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"అన్ని యాప్‌లను చూడడానికి ఉపయోగించే సంజ్ఞకు సంబంధించిన ట్యుటోరియల్‌ను మీరు పూర్తి చేశారు"</string>
-    <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
-    <skip />
+    <string name="tutorial_animation_content_description" msgid="2698816574982370184">"ట్యుటోరియల్ యానిమేషన్, పాజ్ చేసి, మళ్లీ ప్లే చేయడానికి క్లిక్ చేయండి."</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"కీబోర్డ్ బ్యాక్‌లైట్"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$dలో %1$dవ స్థాయి"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"హోమ్ కంట్రోల్స్"</string>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index fe13888..d438e44 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -531,6 +531,7 @@
     <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"วิดเจ็ต"</string>
     <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"หากต้องการเพิ่มทางลัด \"วิดเจ็ต\" โปรดตรวจสอบว่าได้เปิดใช้ \"แสดงวิดเจ็ตในหน้าจอล็อก\" แล้วในการตั้งค่า"</string>
     <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"การตั้งค่า"</string>
+    <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"ปุ่มแสดงภาพพักหน้าจอ"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"สลับผู้ใช้"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"เมนูแบบเลื่อนลง"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ระบบจะลบแอปและข้อมูลทั้งหมดในเซสชันนี้"</string>
@@ -591,8 +592,7 @@
     <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>
-    <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
-    <skip />
+    <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"เปิดการตั้งค่าการแจ้งเตือน"</string>
     <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"หยุดการแจ้งเตือนชั่วคราวโดย \"ห้ามรบกวน\""</string>
     <string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{ไม่มีการแจ้งเตือน}=1{หยุดการแจ้งเตือนชั่วคราวโดย {mode}}=2{หยุดการแจ้งเตือนชั่วคราวโดย {mode} และโหมดอื่นอีก 1 โหมด}other{หยุดการแจ้งเตือนชั่วคราวโดย {mode} และโหมดอื่นอีก # โหมด}}"</string>
     <string name="media_projection_action_text" msgid="3634906766918186440">"เริ่มเลย"</string>
@@ -707,8 +707,7 @@
     <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"การติดตามการเคลื่อนไหวของศีรษะ"</string>
     <string name="volume_ringer_change" msgid="3574969197796055532">"แตะเพื่อเปลี่ยนโหมดเสียงเรียกเข้า"</string>
     <string name="volume_ringer_mode" msgid="6867838048430807128">"โหมดเสียงเรียกเข้า"</string>
-    <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
-    <skip />
+    <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g> แตะเพื่อเปลี่ยนโหมดเสียงเรียกเข้า"</string>
     <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"ปิดเสียง"</string>
     <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"เปิดเสียง"</string>
     <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"สั่น"</string>
@@ -873,9 +872,12 @@
     <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_full_screen" msgid="336048080383640562">"เปลี่ยนจากโหมดแยกหน้าจอเป็นเต็มหน้าจอ"</string>
+    <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+    <skip />
+    <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+    <skip />
+    <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+    <skip />
     <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>
@@ -1438,8 +1440,7 @@
     <string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"ไอคอนการดำเนินการหรือแป้น Meta"</string>
     <string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"ไอคอนเครื่องหมายบวก"</string>
     <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"ปรับแต่ง"</string>
-    <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
-    <skip />
+    <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"รีเซ็ต"</string>
     <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"เสร็จสิ้น"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ไอคอนขยาย"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"หรือ"</string>
@@ -1481,8 +1482,7 @@
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"กดปุ่มดำเนินการบนแป้นพิมพ์"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"ยอดเยี่ยม"</string>
     <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"คุณทำท่าทางสัมผัสเพื่อดูแอปทั้งหมดสำเร็จแล้ว"</string>
-    <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
-    <skip />
+    <string name="tutorial_animation_content_description" msgid="2698816574982370184">"ภาพเคลื่อนไหวของบทแนะนำ คลิกเพื่อหยุดชั่วคราวและเล่นต่อ"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"ไฟแบ็กไลต์ของแป้นพิมพ์"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"ระดับที่ %1$d จาก %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"ระบบควบคุมอุปกรณ์สมาร์ทโฮม"</string>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index 9d79424..ee15260 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -531,6 +531,7 @@
     <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Mga Widget"</string>
     <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Para idagdag ang shortcut na \"Mga Widget,\" tiyaking naka-enable ang \"Ipakita ang mga widget sa lock screen\" sa mga setting."</string>
     <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Mga Setting"</string>
+    <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Button na ipakita ang screensaver"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Magpalit ng user"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"pulldown menu"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Ide-delete ang lahat ng app at data sa session na ito."</string>
@@ -591,8 +592,7 @@
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Mga Notification"</string>
     <string name="notification_section_header_conversations" msgid="821834744538345661">"Mga Pag-uusap"</string>
     <string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"I-clear ang lahat ng silent na notification"</string>
-    <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
-    <skip />
+    <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Buksan ang mga setting ng notification"</string>
     <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Mga notification na na-pause ng Huwag Istorbohin"</string>
     <string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Walang notification}=1{Na-pause ng {mode} ang mga notification}=2{Na-pause ng {mode} at isa pang mode ang mga notification}one{Na-pause ng {mode} at # pang mode ang mga notification}other{Na-pause ng {mode} at # pang mode ang mga notification}}"</string>
     <string name="media_projection_action_text" msgid="3634906766918186440">"Magsimula ngayon"</string>
@@ -707,8 +707,7 @@
     <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Pag-track ng Ulo"</string>
     <string name="volume_ringer_change" msgid="3574969197796055532">"I-tap para baguhin ang ringer mode"</string>
     <string name="volume_ringer_mode" msgid="6867838048430807128">"ringer mode"</string>
-    <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
-    <skip />
+    <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, i-tap para baguhin ang ringer mode"</string>
     <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"i-mute"</string>
     <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"i-unmute"</string>
     <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"i-vibrate"</string>
@@ -873,9 +872,12 @@
     <string name="group_system_lock_screen" msgid="7391191300363416543">"I-lock ang screen"</string>
     <string name="group_system_quick_memo" msgid="3764560265935722903">"Magtala"</string>
     <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Pag-multitask"</string>
-    <string name="system_multitasking_rhs" msgid="8714224917276297810">"Gumamit ng split screen nang nasa kanan ang kasalukuyang app"</string>
-    <string name="system_multitasking_lhs" msgid="8402954791206308783">"Gumamit ng split screen nang nasa kaliwa ang kasalukuyang app"</string>
-    <string name="system_multitasking_full_screen" msgid="336048080383640562">"Lumipat sa full screen mula sa split screen"</string>
+    <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+    <skip />
+    <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+    <skip />
+    <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+    <skip />
     <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Lumipat sa app sa kanan o ibaba habang ginagamit ang split screen"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Lumipat sa app sa kaliwa o itaas habang ginagamit ang split screen"</string>
     <string name="system_multitasking_replace" msgid="7410071959803642125">"Habang nasa split screen: magpalit-palit ng app"</string>
@@ -1438,8 +1440,7 @@
     <string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Icon ng Action o Meta key"</string>
     <string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Icon na plus"</string>
     <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"I-customize"</string>
-    <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
-    <skip />
+    <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"I-reset"</string>
     <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Tapos na"</string>
     <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>
@@ -1481,8 +1482,7 @@
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Pindutin ang action key sa iyong keyboard"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Magaling!"</string>
     <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Nakumpleto mo ang galaw sa pag-view ng lahat ng app"</string>
-    <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
-    <skip />
+    <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Animation ng tutorial, i-click para i-pause at ipagpatuloy ang paglalaro."</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Backlight ng keyboard"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Level %1$d sa %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Mga Home Control"</string>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index 046d973..c12f7b6 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -529,9 +529,9 @@
     <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>
     <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widget\'lar"</string>
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
-    <skip />
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
+    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"\"Widget\'lar\" kısayolunu eklemek için ayarlarda \"Widget\'ları kilit ekranında göster\" seçeneğinin etkinleştirildiğinden emin olun."</string>
+    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Ayarlar"</string>
+    <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
     <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Kullanıcı değiştirme"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"açılır menü"</string>
@@ -709,8 +709,7 @@
     <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Baş Takibi"</string>
     <string name="volume_ringer_change" msgid="3574969197796055532">"Telefon zili modunu değiştirmek için dokunun"</string>
     <string name="volume_ringer_mode" msgid="6867838048430807128">"telefon zili modu"</string>
-    <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
-    <skip />
+    <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, telefon zili modunu değiştirmek için dokunun"</string>
     <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"sesi  kapat"</string>
     <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"sesi aç"</string>
     <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"titreşim"</string>
@@ -875,9 +874,12 @@
     <string name="group_system_lock_screen" msgid="7391191300363416543">"Kilit ekranı"</string>
     <string name="group_system_quick_memo" msgid="3764560265935722903">"Not al"</string>
     <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Çoklu görev"</string>
-    <string name="system_multitasking_rhs" msgid="8714224917276297810">"Sağdaki mevcut uygulamayla birlikte bölünmüş ekranı kullan"</string>
-    <string name="system_multitasking_lhs" msgid="8402954791206308783">"Soldaki mevcut uygulamayla birlikte bölünmüş ekranı kullan"</string>
-    <string name="system_multitasking_full_screen" msgid="336048080383640562">"Bölünmüş ekrandan tam ekrana geç"</string>
+    <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+    <skip />
+    <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+    <skip />
+    <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+    <skip />
     <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Bölünmüş ekran kullanırken sağdaki veya alttaki uygulamaya geçiş yap"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Bölünmüş ekran kullanırken soldaki veya üstteki uygulamaya geçiş yapın"</string>
     <string name="system_multitasking_replace" msgid="7410071959803642125">"Bölünmüş ekran etkinken: Bir uygulamayı başkasıyla değiştir"</string>
@@ -1430,20 +1432,17 @@
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Klavye kısayolları"</string>
     <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Klavye kısayollarını özelleştirin"</string>
     <string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Kısayol kaldırılsın mı?"</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Varsayılan kısayollara sıfırlansın mı?"</string>
     <string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Kısayol atamak için tuşa basın"</string>
     <string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Bu işlem, özel kısayolunuzu kalıcı olarak siler."</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Bu işlem, tüm özel kısayollarınızı kalıcı olarak siler."</string>
     <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>
     <string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"İşlem veya Meta tuşu simgesi"</string>
     <string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Artı simgesi"</string>
     <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Özelleştir"</string>
-    <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
-    <skip />
+    <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Sıfırla"</string>
     <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Bitti"</string>
     <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>
@@ -1453,8 +1452,7 @@
     <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Klavye Ayarları"</string>
     <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Kısayol ayarla"</string>
     <string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Kaldır"</string>
-    <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
-    <skip />
+    <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Evet, sıfırlansın"</string>
     <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"İptal"</string>
     <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Tuşa basın"</string>
     <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Tuş kombinasyonu zaten kullanılıyor. Başka bir tuş deneyin."</string>
@@ -1486,8 +1484,7 @@
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Klavyenizde eylem tuşuna basın"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Tebrikler!"</string>
     <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Tüm uygulamaları görüntüleme hareketini tamamladınız"</string>
-    <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
-    <skip />
+    <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Eğitim animasyonu, oynatmayı duraklatmak ve sürdürmek için tıklayın."</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Klavye aydınlatması"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Seviye %1$d / %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Ev Kontrolleri"</string>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index a414824..c2c3c61 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -529,9 +529,9 @@
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Щоб відкрити додаток за допомогою віджета, вам потрібно буде підтвердити особу. Пам’ятайте також, що бачити віджети можуть усі, навіть коли планшет заблоковано. Можливо, деякі віджети не призначені для заблокованого екрана, і додавати їх на нього може бути небезпечно."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string>
     <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Віджети"</string>
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
-    <skip />
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
+    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Щоб додати ярлик \"Віджети\", переконайтеся, що в налаштуваннях увімкнено опцію \"Показувати віджети на заблокованому екрані\"."</string>
+    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Налаштування"</string>
+    <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
     <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Змінити користувача"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"спадне меню"</string>
@@ -709,8 +709,7 @@
     <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Відстеження рухів голови"</string>
     <string name="volume_ringer_change" msgid="3574969197796055532">"Торкніться, щоб змінити режим дзвінка"</string>
     <string name="volume_ringer_mode" msgid="6867838048430807128">"режим дзвінка"</string>
-    <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
-    <skip />
+    <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>. Натисніть, щоб змінити режим дзвінка."</string>
     <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"вимкнути звук"</string>
     <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"увімкнути звук"</string>
     <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"увімкнути вібросигнал"</string>
@@ -875,9 +874,12 @@
     <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_full_screen" msgid="336048080383640562">"Перейти з розділення екрана на весь екран"</string>
+    <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+    <skip />
+    <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+    <skip />
+    <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+    <skip />
     <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>
@@ -1430,20 +1432,17 @@
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Комбінації клавіш"</string>
     <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Налаштуйте комбінації клавіш"</string>
     <string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Видалити комбінацію клавіш?"</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Відновити комбінації клавіш за умовчанням?"</string>
     <string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Натисніть клавішу, щоб призначити комбінацію клавіш"</string>
     <string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Вашу власну комбінацію клавіш буде видалено назавжди."</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Усі ваші власні комбінації клавіш буде видалено назавжди."</string>
     <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>
     <string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Значок клавіші дії або метаклавіші"</string>
     <string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Значок \"плюс\""</string>
     <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Налаштувати"</string>
-    <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
-    <skip />
+    <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Скинути"</string>
     <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Готово"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Значок розгортання"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"або"</string>
@@ -1453,8 +1452,7 @@
     <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Налаштування клавіатури"</string>
     <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Налаштувати комбінацію клавіш"</string>
     <string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Видалити"</string>
-    <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
-    <skip />
+    <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Так, відновити"</string>
     <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Скасувати"</string>
     <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Натисніть клавішу"</string>
     <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Комбінація клавіш уже використовується. Спробуйте іншу клавішу."</string>
@@ -1486,8 +1484,7 @@
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Натисніть клавішу дії на клавіатурі"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Чудово!"</string>
     <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Ви виконали жест для перегляду всіх додатків"</string>
-    <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
-    <skip />
+    <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Навчальна анімація. Натисніть, щоб призупинити або відновити відтворення."</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Підсвічування клавіатури"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Рівень %1$d з %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Автоматизація дому"</string>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index 2980b2f..8b3b9a0 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -531,6 +531,7 @@
     <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"ویجیٹس"</string>
     <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"\"ویجیٹس\" شارٹ کٹ شامل کرنے کے لیے، یقینی بنائیں کہ \"مقفل اسکرین پر ویجیٹس دکھائیں\" ترتیبات میں فعال ہے۔"</string>
     <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"ترتیبات"</string>
+    <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"اسکرین سیور بٹن دکھائیں"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"صارف سوئچ کریں"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"پل ڈاؤن مینیو"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"اس سیشن میں موجود سبھی ایپس اور ڈیٹا کو حذف کر دیا جائے گا۔"</string>
@@ -591,8 +592,7 @@
     <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>
-    <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
-    <skip />
+    <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"اطلاعات کی ترتیبات کھولیں"</string>
     <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"\'ڈسٹرب نہ کریں\' کے ذریعے اطلاعات کو موقوف کیا گیا"</string>
     <string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{کوئی اطلاع نہیں ہے}=1{{mode} کی طرف سے اطلاعات کو روک دیا گیا ہے}=2{{mode} اور ایک دوسرے موڈ کے ذریعہ اطلاعات کو روک دیا گیا ہے}other{{mode} اور # دیگر طریقوں کے ذریعے اطلاعات کو روک دیا گیا ہے}}"</string>
     <string name="media_projection_action_text" msgid="3634906766918186440">"ابھی شروع کریں"</string>
@@ -707,8 +707,7 @@
     <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"سر کی ٹریکنگ"</string>
     <string name="volume_ringer_change" msgid="3574969197796055532">"رنگر وضع تبدیل کرنے کیلئے تھپتھپائیں"</string>
     <string name="volume_ringer_mode" msgid="6867838048430807128">"رنگر موڈ"</string>
-    <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
-    <skip />
+    <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"رنگر وضع تبدیل کرنے کیلئے <xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g> تھپتھپائیں"</string>
     <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"خاموش کریں"</string>
     <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"غیر خاموش کریں"</string>
     <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"وائبریٹ"</string>
@@ -873,9 +872,12 @@
     <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_full_screen" msgid="336048080383640562">"اسپلٹ اسکرین سے پوری سکرین پر سوئچ کریں"</string>
+    <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+    <skip />
+    <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+    <skip />
+    <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+    <skip />
     <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>
@@ -1438,8 +1440,7 @@
     <string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"‏کارروائی یا Meta کلید کا آئیکن"</string>
     <string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"پلس کا آئیکن"</string>
     <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"حسب ضرورت بنائیں"</string>
-    <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
-    <skip />
+    <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"ری سیٹ کریں"</string>
     <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"ہو گیا"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"آئیکن پھیلائیں"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"یا"</string>
@@ -1481,8 +1482,7 @@
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"اپنے کی بورڈ پر ایکشن کلید دبائیں"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"بہت خوب!"</string>
     <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"آپ نے سبھی ایپس دیکھیں کا اشارہ مکمل کر لیا ہے"</string>
-    <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
-    <skip />
+    <string name="tutorial_animation_content_description" msgid="2698816574982370184">"ٹیوٹوریل اینیمیشن، روکنے کے لیے کلک کریں اور چلانا دوبارہ شروع کریں۔"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"کی بورڈ بیک لائٹ"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"‏%2$d میں سے ‎%1$d کا لیول"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"ہوم کنٹرولز"</string>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index 9057926..dc01a49 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -531,6 +531,8 @@
     <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Vidjetlar"</string>
     <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"“Vidjetlar” yorligʻini qoʻshish uchun sozlamalarda “Vidjetlarni ekran qulfida chiqarish” yoqilganini tekshiring."</string>
     <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Sozlamalar"</string>
+    <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
+    <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Foydalanuvchini almashtirish"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"tortib tushiriladigan menyu"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Ushbu seansdagi barcha ilovalar va ma’lumotlar o‘chirib tashlanadi."</string>
@@ -591,8 +593,7 @@
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Bildirishnomalar"</string>
     <string name="notification_section_header_conversations" msgid="821834744538345661">"Suhbatlar"</string>
     <string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Barcha sokin bildirishnomalarni tozalash"</string>
-    <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
-    <skip />
+    <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Bildirishnoma sozlamalarini ochish"</string>
     <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Bezovta qilinmasin rejimida bildirishnomalar pauza qilinadi"</string>
     <string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Bildirishnomalar yoʻq}=1{{mode} rejimi bildirishnomalarni pauza qilgan}=2{{mode} va yana bitta boshqa rejim bildirishnomalarni pauza qilgan}other{{mode} va # ta boshqa rejim bildirishnomalarni pauza qilgan}}"</string>
     <string name="media_projection_action_text" msgid="3634906766918186440">"Boshlash"</string>
@@ -872,9 +873,12 @@
     <string name="group_system_lock_screen" msgid="7391191300363416543">"Ekran qulfi"</string>
     <string name="group_system_quick_memo" msgid="3764560265935722903">"Qayd yaratish"</string>
     <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Multi-vazifalilik"</string>
-    <string name="system_multitasking_rhs" msgid="8714224917276297810">"Ekranni ajratib, joriy ilovani oʻngga joylash"</string>
-    <string name="system_multitasking_lhs" msgid="8402954791206308783">"Ekranni ajratib, joriy ilovani chapga joylash"</string>
-    <string name="system_multitasking_full_screen" msgid="336048080383640562">"Ajratilgan ekran rejimidan butun ekranga almashtirish"</string>
+    <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+    <skip />
+    <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+    <skip />
+    <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+    <skip />
     <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Ajratilgan ekranda oʻngdagi yoki pastdagi ilovaga almashish"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Ajratilgan ekranda chapdagi yoki yuqoridagi ilovaga almashish"</string>
     <string name="system_multitasking_replace" msgid="7410071959803642125">"Ajratilgan rejimda ilovalarni oʻzaro almashtirish"</string>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index 6c7c5f6..1dd6042 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -529,9 +529,9 @@
     <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>
     <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Tiện ích"</string>
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
-    <skip />
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
+    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Để thêm phím tắt \"Tiện ích\", hãy nhớ bật tuỳ chọn \"Hiện tiện ích trên màn hình khoá\" trong phần cài đặt."</string>
+    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Cài đặt"</string>
+    <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
     <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Chuyển đổi người dùng"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"trình đơn kéo xuống"</string>
@@ -593,8 +593,7 @@
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Thông báo"</string>
     <string name="notification_section_header_conversations" msgid="821834744538345661">"Cuộc trò chuyện"</string>
     <string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Xóa tất cả thông báo im lặng"</string>
-    <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
-    <skip />
+    <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Mở phần cài đặt thông báo"</string>
     <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Chế độ Không làm phiền đã tạm dừng thông báo"</string>
     <string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Không có thông báo}=1{{mode} đã tạm dừng thông báo}=2{{mode} và một chế độ khác đã tạm dừng thông báo}other{{mode} và # chế độ khác đã tạm dừng thông báo}}"</string>
     <string name="media_projection_action_text" msgid="3634906766918186440">"Bắt đầu ngay"</string>
@@ -709,8 +708,7 @@
     <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Theo dõi chuyển động của đầu"</string>
     <string name="volume_ringer_change" msgid="3574969197796055532">"Nhấn để thay đổi chế độ chuông"</string>
     <string name="volume_ringer_mode" msgid="6867838048430807128">"chế độ chuông"</string>
-    <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
-    <skip />
+    <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, nhấn để thay đổi chế độ chuông"</string>
     <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"tắt tiếng"</string>
     <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"bật tiếng"</string>
     <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"rung"</string>
@@ -875,9 +873,12 @@
     <string name="group_system_lock_screen" msgid="7391191300363416543">"Màn hình khoá"</string>
     <string name="group_system_quick_memo" msgid="3764560265935722903">"Tạo ghi chú"</string>
     <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Đa nhiệm"</string>
-    <string name="system_multitasking_rhs" msgid="8714224917276297810">"Dùng tính năng chia đôi màn hình, trong đó ứng dụng hiện tại ở bên phải"</string>
-    <string name="system_multitasking_lhs" msgid="8402954791206308783">"Dùng tính năng chia đôi màn hình, trong đó ứng dụng hiện tại ở bên trái"</string>
-    <string name="system_multitasking_full_screen" msgid="336048080383640562">"Chuyển từ chế độ chia đôi màn hình sang chế độ toàn màn hình"</string>
+    <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+    <skip />
+    <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+    <skip />
+    <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+    <skip />
     <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Chuyển sang ứng dụng bên phải hoặc ở dưới khi đang chia đôi màn hình"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Chuyển sang ứng dụng bên trái hoặc ở trên khi đang chia đôi màn hình"</string>
     <string name="system_multitasking_replace" msgid="7410071959803642125">"Trong chế độ chia đôi màn hình: thay một ứng dụng bằng ứng dụng khác"</string>
@@ -1430,20 +1431,17 @@
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Phím tắt"</string>
     <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Tuỳ chỉnh phím tắt"</string>
     <string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Xoá lối tắt?"</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Đặt lại về phím tắt mặc định?"</string>
     <string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Nhấn phím để chỉ định lối tắt"</string>
     <string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Thao tác này sẽ xoá vĩnh viễn lối tắt tuỳ chỉnh của bạn."</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Thao tác này sẽ xoá vĩnh viễn mọi phím tắt tuỳ chỉnh của bạn."</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Tìm lối tắt"</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>
     <string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Biểu tượng phím Meta (phím hành động)"</string>
     <string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Biểu tượng dấu cộng"</string>
     <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Tuỳ chỉnh"</string>
-    <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
-    <skip />
+    <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Đặt lại"</string>
     <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Xong"</string>
     <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>
@@ -1453,8 +1451,7 @@
     <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Cài đặt bàn phím"</string>
     <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Đặt phím tắt"</string>
     <string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Xoá"</string>
-    <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
-    <skip />
+    <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Có, đặt lại"</string>
     <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Huỷ"</string>
     <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Nhấn phím"</string>
     <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Tổ hợp phím đã được sử dụng. Hãy thử một phím khác."</string>
@@ -1486,8 +1483,7 @@
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Nhấn phím hành động trên bàn phím"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Rất tốt!"</string>
     <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Bạn đã hoàn tất cử chỉ xem tất cả các ứng dụng"</string>
-    <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
-    <skip />
+    <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Ảnh động trong phần hướng dẫn, nhấp để tạm dừng và tiếp tục phát."</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Đèn nền bàn phím"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Độ sáng %1$d/%2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Điều khiển nhà"</string>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index d37fe90..e18fcec 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -531,6 +531,7 @@
     <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"微件"</string>
     <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"如要添加“微件”快捷方式,请确保已在设置中启用“在锁屏状态下显示微件”。"</string>
     <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"设置"</string>
+    <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"“显示屏保”按钮"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"切换用户"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"下拉菜单"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"此会话中的所有应用和数据都将被删除。"</string>
@@ -591,8 +592,7 @@
     <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>
-    <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
-    <skip />
+    <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"打开通知设置"</string>
     <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"勿扰模式暂停的通知"</string>
     <string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{无通知}=1{{mode}暂停了通知}=2{{mode}和另外 1 种模式暂停了通知}other{{mode}和另外 # 种模式暂停了通知}}"</string>
     <string name="media_projection_action_text" msgid="3634906766918186440">"立即开始"</string>
@@ -707,8 +707,7 @@
     <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"头部跟踪"</string>
     <string name="volume_ringer_change" msgid="3574969197796055532">"点按即可更改振铃器模式"</string>
     <string name="volume_ringer_mode" msgid="6867838048430807128">"响铃模式"</string>
-    <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
-    <skip />
+    <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>,点按即可更改响铃模式"</string>
     <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"静音"</string>
     <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"取消静音"</string>
     <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"振动"</string>
@@ -873,9 +872,12 @@
     <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_full_screen" msgid="336048080383640562">"从分屏模式切换为全屏"</string>
+    <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+    <skip />
+    <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+    <skip />
+    <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+    <skip />
     <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>
@@ -1438,8 +1440,7 @@
     <string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"操作键或元键图标"</string>
     <string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"加号图标"</string>
     <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"自定义"</string>
-    <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
-    <skip />
+    <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"重置"</string>
     <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"完成"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"展开图标"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"或"</string>
@@ -1481,8 +1482,7 @@
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"按键盘上的快捷操作按键"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"非常棒!"</string>
     <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"您已完成“查看所有应用”手势教程"</string>
-    <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
-    <skip />
+    <string name="tutorial_animation_content_description" msgid="2698816574982370184">"教程动画,点击可暂停和继续播放。"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"键盘背光"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"第 %1$d 级,共 %2$d 级"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"家居控制"</string>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index 9b16360..871bd31 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -529,10 +529,9 @@
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"如要使用小工具開啟應用程式,系統會要求你驗證身分。請注意,所有人都能查看小工具,即使平板電腦已鎖定亦然。部分小工具可能不適用於上鎖畫面,新增至這裡可能會有安全疑慮。"</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"知道了"</string>
     <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"小工具"</string>
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
-    <skip />
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
-    <skip />
+    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"如要新增「小工具」捷徑,請確保在設定中已啟用「在上鎖畫面顯示小工具」。"</string>
+    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"設定"</string>
+    <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"顯示螢幕保護程式按鈕"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"切換使用者"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"下拉式選單"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"這個工作階段中的所有應用程式和資料都會被刪除。"</string>
@@ -593,8 +592,7 @@
     <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>
-    <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
-    <skip />
+    <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"打開通知設定"</string>
     <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"「請勿騷擾」模式已將通知暫停"</string>
     <string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{沒有通知}=1{{mode}已暫停通知}=2{{mode}和另外一個模式已暫停通知}other{{mode}和另外 # 個模式已暫停通知}}"</string>
     <string name="media_projection_action_text" msgid="3634906766918186440">"立即開始"</string>
@@ -709,8 +707,7 @@
     <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"頭部追蹤"</string>
     <string name="volume_ringer_change" msgid="3574969197796055532">"輕按即可變更響鈴模式"</string>
     <string name="volume_ringer_mode" msgid="6867838048430807128">"響鈴模式"</string>
-    <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
-    <skip />
+    <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>,輕按以變更響鈴模式"</string>
     <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"靜音"</string>
     <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"取消靜音"</string>
     <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"震動"</string>
@@ -875,9 +872,12 @@
     <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_full_screen" msgid="336048080383640562">"將分割螢幕切換為全螢幕"</string>
+    <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+    <skip />
+    <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+    <skip />
+    <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+    <skip />
     <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>
@@ -1430,20 +1430,17 @@
     <string name="shortcut_helper_title" msgid="8567500639300970049">"鍵盤快速鍵"</string>
     <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"自訂鍵盤快速鍵"</string>
     <string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"要移除快速鍵嗎?"</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"要重設至預設捷徑嗎?"</string>
     <string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"按鍵即可指派快速鍵"</string>
     <string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"這將永久刪除你的自訂快速鍵。"</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"這將永久刪除你的所有自訂捷徑。"</string>
     <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>
     <string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"快捷操作鍵或修飾鍵圖示"</string>
     <string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"加號圖示"</string>
     <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"自訂"</string>
-    <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
-    <skip />
+    <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"重設"</string>
     <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"完成"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"展開圖示"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"或"</string>
@@ -1453,8 +1450,7 @@
     <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"鍵盤設定"</string>
     <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"設定快速鍵"</string>
     <string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"移除"</string>
-    <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
-    <skip />
+    <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"是,請重設"</string>
     <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"取消"</string>
     <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"按鍵"</string>
     <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"此按鍵組合已在使用,請改用其他按鍵。"</string>
@@ -1486,8 +1482,7 @@
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"按下鍵盤上的快捷操作鍵"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"做得好!"</string>
     <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"你已完成「查看所有應用程式」手勢的教學課程"</string>
-    <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
-    <skip />
+    <string name="tutorial_animation_content_description" msgid="2698816574982370184">"教學動畫,按一下以暫停和繼續播放。"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"鍵盤背光"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"第 %1$d 級,共 %2$d 級"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"智能家居"</string>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index 49111ea..33411754 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -529,9 +529,9 @@
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"如要使用小工具開啟應用程式,需先驗證身分。請留意,即使平板電腦已鎖定,所有人都還是能查看小工具。某些小工具可能不適用於螢幕鎖定畫面,新增到此可能會有安全疑慮。"</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"我知道了"</string>
     <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"小工具"</string>
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
-    <skip />
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
+    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"如要新增「小工具」捷徑,請務必前往設定啟用「在螢幕鎖定畫面上顯示小工具」。"</string>
+    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"設定"</string>
+    <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
     <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"切換使用者"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"下拉式選單"</string>
@@ -593,8 +593,7 @@
     <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>
-    <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
-    <skip />
+    <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"開啟通知設定"</string>
     <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"「零打擾」模式已將通知設為暫停"</string>
     <string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{沒有通知}=1{「{mode}」模式已將通知設為暫停}=2{「{mode}」和另一個模式已將通知設為暫停}other{「{mode}」和另外 # 個模式已將通知設為暫停}}"</string>
     <string name="media_projection_action_text" msgid="3634906766918186440">"立即開始"</string>
@@ -709,8 +708,7 @@
     <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"頭部追蹤"</string>
     <string name="volume_ringer_change" msgid="3574969197796055532">"輕觸即可變更鈴聲模式"</string>
     <string name="volume_ringer_mode" msgid="6867838048430807128">"鈴聲模式"</string>
-    <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
-    <skip />
+    <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>,輕觸即可變更鈴聲模式"</string>
     <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"靜音"</string>
     <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"取消靜音"</string>
     <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"震動"</string>
@@ -875,9 +873,12 @@
     <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_full_screen" msgid="336048080383640562">"從分割畫面切換到完整畫面"</string>
+    <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+    <skip />
+    <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+    <skip />
+    <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+    <skip />
     <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>
@@ -1430,20 +1431,17 @@
     <string name="shortcut_helper_title" msgid="8567500639300970049">"鍵盤快速鍵"</string>
     <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"自訂鍵盤快速鍵"</string>
     <string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"要移除快速鍵嗎?"</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"要重設為預設值嗎?"</string>
     <string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"按下按鍵即可指派快速鍵"</string>
     <string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"這項操作會永久刪除自訂快速鍵。"</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"這麼做會永久刪除所有快速鍵。"</string>
     <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>
     <string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"快捷操作鍵或修飾鍵圖示"</string>
     <string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"加號圖示"</string>
     <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"自訂"</string>
-    <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
-    <skip />
+    <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"重設"</string>
     <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"完成"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"展開圖示"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"或"</string>
@@ -1453,8 +1451,7 @@
     <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"鍵盤設定"</string>
     <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"設定快速鍵"</string>
     <string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"移除"</string>
-    <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
-    <skip />
+    <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"是,請重設"</string>
     <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"取消"</string>
     <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"按下按鍵"</string>
     <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"這個按鍵組合已在使用中,請改用其他按鍵。"</string>
@@ -1486,8 +1483,7 @@
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"按下鍵盤上的快捷操作鍵"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"非常好!"</string>
     <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"你已完成「查看所有應用程式」手勢教學課程"</string>
-    <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
-    <skip />
+    <string name="tutorial_animation_content_description" msgid="2698816574982370184">"教學課程動畫,按一下即可暫停和繼續播放。"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"鍵盤背光"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"第 %1$d 級,共 %2$d 級"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"居家控制"</string>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 9d4f70c..a9fa1ba 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -529,9 +529,9 @@
     <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>
     <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Amawijethi"</string>
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
-    <skip />
-    <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
+    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Ukuze ufake isinqamuleli esithi \"Amawijethi\", qinisekisa ukuthi okuthi \"Bonisa amawijethi esikrinini sokukhiya\" kunikwe amandla kumasethingi."</string>
+    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Amasethingi"</string>
+    <!-- no translation found for accessibility_glanceable_hub_to_dream_button (7552776300297055307) -->
     <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Shintsha umsebenzisi"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"imenyu yokudonsela phansi"</string>
@@ -709,8 +709,7 @@
     <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Ukulandelela Ikhanda"</string>
     <string name="volume_ringer_change" msgid="3574969197796055532">"Thepha ukuze ushintshe imodi yokukhala"</string>
     <string name="volume_ringer_mode" msgid="6867838048430807128">"imodi yokukhala"</string>
-    <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
-    <skip />
+    <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, thepha ukuze ushintshe imodi yokukhala"</string>
     <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"thulisa"</string>
     <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"susa ukuthula"</string>
     <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"dlidliza"</string>
@@ -875,9 +874,12 @@
     <string name="group_system_lock_screen" msgid="7391191300363416543">"Khiya isikrini"</string>
     <string name="group_system_quick_memo" msgid="3764560265935722903">"Thatha inothi"</string>
     <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Ukwenza imisebenzi eminingi"</string>
-    <string name="system_multitasking_rhs" msgid="8714224917276297810">"Sebenzisa isikrini esihlukanisayo nge-app yamanje kwesokudla"</string>
-    <string name="system_multitasking_lhs" msgid="8402954791206308783">"Sebenzisa isikrini sokuhlukanisa nge-app yamanje kwesokunxele"</string>
-    <string name="system_multitasking_full_screen" msgid="336048080383640562">"Shintsha usuka ekuhlukaniseni isikrini uye kusikrini esigcwele"</string>
+    <!-- no translation found for system_multitasking_rhs (8779289852395243004) -->
+    <skip />
+    <!-- no translation found for system_multitasking_lhs (7348595296208696452) -->
+    <skip />
+    <!-- no translation found for system_multitasking_full_screen (4940465971687159429) -->
+    <skip />
     <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Shintshela ku-app ngakwesokudla noma ngezansi ngenkathi usebenzisa uhlukanisa isikrini"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Shintshela ku-app ngakwesokunxele noma ngaphezulu ngenkathi usebenzisa ukuhlukanisa isikrini"</string>
     <string name="system_multitasking_replace" msgid="7410071959803642125">"Ngesikhathi sokuhlukaniswa kwesikrini: shintsha i-app ngenye"</string>
@@ -1430,20 +1432,17 @@
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Izinqamuleli zekhibhodi"</string>
     <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Hlela izinqamuleli zekhibhodi ngendlela oyifisayo"</string>
     <string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Susa isinqamuleli?"</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Setha kabusha ubuyele kokuzenzakalelayo?"</string>
     <string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Cindezela ukhiye ukuze unikeze isinqamuleli"</string>
     <string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Lokhu kuzosula isinqamuleli sakho somuntu ngamunye unomphela."</string>
-    <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
-    <skip />
+    <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Lokhu kuzosula unomphela zonke izinqamuleli zakho zangokwezifiso."</string>
     <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>
     <string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Isithonjana sesenzo noma seMeta"</string>
     <string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Isithonjana sesengezo"</string>
     <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Enza ngendlela oyifisayo"</string>
-    <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
-    <skip />
+    <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Setha kabusha"</string>
     <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Kwenziwe"</string>
     <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>
@@ -1453,8 +1452,7 @@
     <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Amasethingi Ekhibhodi"</string>
     <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Setha isinqamuleli"</string>
     <string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Susa"</string>
-    <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
-    <skip />
+    <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Yebo, setha kabusha"</string>
     <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Khansela"</string>
     <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Cindezela ukhiye"</string>
     <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Inhlanganisela yokhiye isiyasetshenziswa kakade. Zama omunye ukhiye."</string>
@@ -1486,8 +1484,7 @@
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Cindezela inkinobho yokufinyelela kukhibhodi yakho"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Wenze kahle!"</string>
     <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Uqedele ukunyakazisa kokubuka onke ama-app."</string>
-    <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
-    <skip />
+    <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Okopopayi okokufundisa, chofoza ukuze umise kancane futhi uqalise kabusha ukudlala."</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Ilambu lekhibhodi"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Ileveli %1$d ka-%2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Izilawuli Zasekhaya"</string>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 5894f29..35cfd08 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1760,6 +1760,7 @@
     <dimen name="wallet_button_vertical_padding">8dp</dimen>
 
     <!-- Ongoing activity chip -->
+    <dimen name="ongoing_activity_chip_max_text_width">74dp</dimen>
     <!-- The activity chip side padding, used with the default phone icon. -->
     <dimen name="ongoing_activity_chip_side_padding">12dp</dimen>
     <!-- The activity chip side padding, used with an icon that has embedded padding (e.g. if the icon comes from the notification's smallIcon field). If the icon has padding, the chip itself can have less padding. -->
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index 88ed4e3..c06b078 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -215,27 +215,30 @@
     <item type="id" name="backlight_icon" />
 
     <!-- IDs for use in the keyguard/lockscreen scene -->
-    <item type="id" name="keyguard_root_view" />
-    <item type="id" name="keyguard_indication_area" />
-    <item type="id" name="keyguard_indication_text" />
-    <item type="id" name="keyguard_indication_text_bottom" />
-    <item type="id" name="nssl_guideline" />
-    <item type="id" name="nssl_placeholder" />
+    <item type="id" name="accessibility_actions_view" />
+    <item type="id" name="ambient_indication_container" />
     <item type="id" name="aod_notification_icon_container" />
-    <item type="id" name="split_shade_guideline" />
-    <item type="id" name="lock_icon" />
-    <item type="id" name="lock_icon_bg" />
     <item type="id" name="burn_in_layer" />
     <item type="id" name="burn_in_layer_empty_view" />
     <item type="id" name="communal_tutorial_indicator" />
+    <item type="id" name="end_button" />
+    <item type="id" name="lock_icon" />
+    <item type="id" name="lock_icon_bg" />
+    <item type="id" name="keyguard_indication_area" />
+    <item type="id" name="keyguard_indication_text" />
+    <item type="id" name="keyguard_indication_text_bottom" />
+    <item type="id" name="keyguard_root_view" />
+    <item type="id" name="keyguard_settings_button" />
+    <item type="id" name="nssl_guideline" />
+    <item type="id" name="nssl_placeholder" />
     <item type="id" name="nssl_placeholder_barrier_bottom" />
-    <item type="id" name="ambient_indication_container" />
-    <item type="id" name="status_view_media_container" />
-    <item type="id" name="smart_space_barrier_bottom" />
     <item type="id" name="small_clock_guideline_top" />
+    <item type="id" name="smart_space_barrier_bottom" />
+    <item type="id" name="split_shade_guideline" />
+    <item type="id" name="start_button" />
+    <item type="id" name="status_view_media_container" />
     <item type="id" name="weather_clock_date_and_icons_barrier_bottom" />
     <item type="id" name="weather_clock_bc_smartspace_bottom" />
-    <item type="id" name="accessibility_actions_view" />
 
     <!-- Privacy dialog -->
     <item type="id" name="privacy_dialog_close_app_button" />
@@ -280,7 +283,7 @@
     <item type="id" name="udfps_accessibility_overlay_top_guideline" />
 
     <!-- Ids for communal hub widgets -->
-    <item type="id" name="communal_widget_disposable_tag"/>
+    <item type="id" name="communal_widget_listener_tag"/>
 
     <!-- snapshot view-binding IDs -->
     <item type="id" name="snapshot_view_binding" />
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 05c13f2..56aaf4c 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1950,6 +1950,21 @@
     <!-- Text displayed indicating that the user might be able to use satellite SOS. -->
     <string name="satellite_emergency_only_carrier_text">Emergency calls or SOS</string>
 
+    <!-- Content description skeleton. Input strings should be carrier name and signal bar description [CHAR LIMIT=NONE]-->
+    <string name="accessibility_phone_string_format"><xliff:g id="carrier_name" example="Carrier Name">%1$s</xliff:g>, <xliff:g id="signal_strength_description" example="two bars">%2$s</xliff:g>.</string>
+    <!-- Content description describing 0 signal bars. [CHAR LIMIT=NONE] -->
+    <string name="accessibility_no_signal">no signal</string>
+    <!-- Content description describing 1 signal bar. [CHAR LIMIT=NONE] -->
+    <string name="accessibility_one_bar">one bar</string>
+    <!-- Content description describing 2 signal bars. [CHAR LIMIT=NONE] -->
+    <string name="accessibility_two_bars">two bars</string>
+    <!-- Content description describing 3 signal bars. [CHAR LIMIT=NONE] -->
+    <string name="accessibility_three_bars">three bars</string>
+    <!-- Content description describing 4 signal bars. [CHAR LIMIT=NONE] -->
+    <string name="accessibility_four_bars">four bars</string>
+    <!-- Content description describing full signal bars. [CHAR LIMIT=NONE] -->
+    <string name="accessibility_signal_full">signal full</string>
+
     <!-- Accessibility label for managed profile icon (not shown on screen) [CHAR LIMIT=NONE] -->
     <string name="accessibility_managed_profile">Work profile</string>
 
@@ -2046,8 +2061,9 @@
     <!-- Text shown in notification guts for conversation notifications that don't implement the full feature -->
     <string name="no_shortcut"><xliff:g id="app_name" example="YouTube">%1$s</xliff:g> doesn\u2019t support conversation features</string>
 
+    <!-- TODO: b/381099727 - Mark translatable once feedback layout is finalized. -->
     <!-- [CHAR LIMIT=80] Text shown in feedback button in notification guts for a bundled notification -->
-    <string name="notification_guts_bundle_feedback">Provide Bundle Feedback</string>
+    <string name="notification_guts_bundle_feedback" translatable="false">Provide Bundle Feedback</string>
 
     <!-- Notification: Control panel: Label that displays when the app's notifications cannot be blocked. -->
     <string name="notification_unblockable_desc">These notifications can\'t be modified.</string>
@@ -2575,8 +2591,6 @@
     <!-- Tuner string -->
     <!-- Tuner string -->
 
-    <!-- Message shown during shutdown when Find My Device with Dead Battery Finder is active  [CHAR LIMIT=300] -->
-    <string name="finder_active">You can locate this phone with Find My Device even when powered off</string>
     <!-- Shutdown Progress Dialog. This is shown if the user chooses to power off the phone. [CHAR LIMIT=60] -->
     <string name="shutdown_progress">Shutting down\u2026</string>
 
@@ -3918,6 +3932,8 @@
     <string name="touchpad_tutorial_recent_apps_gesture_button">View recent apps</string>
     <!-- Label for button finishing touchpad tutorial [CHAR LIMIT=NONE] -->
     <string name="touchpad_tutorial_done_button">Done</string>
+    <!-- Screen title after gesture was not done correctly [CHAR LIMIT=NONE] -->
+    <string name="gesture_error_title">Try again!</string>
     <!-- BACK GESTURE -->
     <!-- Touchpad back gesture action name in tutorial [CHAR LIMIT=NONE] -->
     <string name="touchpad_back_gesture_action_title">Go back</string>
@@ -3927,6 +3943,8 @@
     <string name="touchpad_back_gesture_success_title">Nice!</string>
     <!-- Text shown to the user after they complete back gesture tutorial [CHAR LIMIT=NONE] -->
     <string name="touchpad_back_gesture_success_body">You completed the go back gesture.</string>
+    <!-- Text shown to the user after back gesture was not done correctly [CHAR LIMIT=NONE] -->
+    <string name="touchpad_back_gesture_error_body">To go back using your touchpad, swipe left or right using three fingers</string>
     <!-- HOME GESTURE -->
     <!-- Touchpad home gesture action name in tutorial [CHAR LIMIT=NONE] -->
     <string name="touchpad_home_gesture_action_title">Go home</string>
@@ -3936,6 +3954,8 @@
     <string name="touchpad_home_gesture_success_title">Great job!</string>
     <!-- Text shown to the user after they complete home gesture tutorial [CHAR LIMIT=NONE] -->
     <string name="touchpad_home_gesture_success_body">You completed the go home gesture</string>
+    <!-- Text shown to the user after home gesture was not done correctly [CHAR LIMIT=NONE] -->
+    <string name="touchpad_home_gesture_error_body">Swipe up with three fingers on your touchpad to go to your home screen</string>
     <!-- RECENT APPS GESTURE -->
     <!-- Touchpad recent apps gesture action name in tutorial [CHAR LIMIT=NONE] -->
     <string name="touchpad_recent_apps_gesture_action_title">View recent apps</string>
@@ -3945,6 +3965,8 @@
     <string name="touchpad_recent_apps_gesture_success_title">Great job!</string>
     <!-- Text shown to the user after they complete recent apps gesture tutorial [CHAR LIMIT=NONE] -->
     <string name="touchpad_recent_apps_gesture_success_body">You completed the view recent apps gesture.</string>
+    <!-- Text shown to the user after recent gesture was not done correctly [CHAR LIMIT=NONE] -->
+    <string name="touchpad_recent_gesture_error_body">To view recent apps, swipe up and hold using three fingers on your touchpad</string>
 
     <!-- KEYBOARD TUTORIAL-->
     <!-- Action key tutorial title [CHAR LIMIT=NONE] -->
@@ -3955,6 +3977,9 @@
     <string name="tutorial_action_key_success_title">Well done!</string>
     <!-- Text shown to the user after they complete action key tutorial [CHAR LIMIT=NONE] -->
     <string name="tutorial_action_key_success_body">You completed the view all apps gesture</string>
+    <!-- Text shown to the user after action key tutorial was not done correctly [CHAR LIMIT=NONE] -->
+    <string name="touchpad_action_key_error_body">Press the action key on your keyboard to view all of your apps</string>
+
     <!-- Content description for the animation playing during the tutorial. The user can click the animation to pause and unpause playback. [CHAR LIMIT=NONE] -->
     <string name="tutorial_animation_content_description">Tutorial animation, click to pause and resume play.</string>
 
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 12f6e69..3156a50 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -84,6 +84,16 @@
         <item name="android:textColor">?android:attr/colorPrimary</item>
     </style>
 
+    <!-- Style for a status bar chip text that has a maximum width. Since there's so little room in
+         the status bar chip area, don't ellipsize the text and instead just fade it out a bit at
+         the end. -->
+    <style name="StatusBar.Chip.Text.LimitedWidth">
+        <item name="android:ellipsize">none</item>
+        <item name="android:requiresFadingEdge">horizontal</item>
+        <item name="android:fadingEdgeLength">@dimen/ongoing_activity_chip_text_fading_edge_length</item>
+        <item name="android:maxWidth">@dimen/ongoing_activity_chip_max_text_width</item>
+    </style>
+
     <style name="Chipbar" />
 
     <style name="Chipbar.Text" parent="@*android:style/TextAppearance.DeviceDefault.Notification.Title">
diff --git a/packages/SystemUI/src/com/android/keyguard/EmptyLockIconViewController.kt b/packages/SystemUI/src/com/android/keyguard/EmptyLockIconViewController.kt
deleted file mode 100644
index 306d682..0000000
--- a/packages/SystemUI/src/com/android/keyguard/EmptyLockIconViewController.kt
+++ /dev/null
@@ -1,64 +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.keyguard
-
-import android.view.MotionEvent
-import android.view.View
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.ui.view.KeyguardRootView
-import com.android.systemui.res.R
-import dagger.Lazy
-import javax.inject.Inject
-
-/**
- * Lock icon view logic now lives in DeviceEntryIconViewBinder and ViewModels. Icon is positioned in
- * [com.android.systemui.keyguard.ui.view.layout.sections.DefaultDeviceEntrySection].
- *
- * This class is to bridge the gap between the logic when the DeviceEntryUdfpsRefactor is enabled
- * and the KeyguardBottomAreaRefactor is NOT enabled. This class can and should be removed when both
- * flags are enabled.
- */
-@SysUISingleton
-class EmptyLockIconViewController
-@Inject
-constructor(private val keyguardRootView: Lazy<KeyguardRootView>) : LockIconViewController {
-    private val deviceEntryIconViewId = R.id.device_entry_icon_view
-
-    override fun setLockIconView(lockIconView: View) {
-        // no-op
-    }
-
-    override fun getTop(): Float {
-        return keyguardRootView.get().getViewById(deviceEntryIconViewId)?.top?.toFloat() ?: 0f
-    }
-
-    override fun getBottom(): Float {
-        return keyguardRootView.get().getViewById(deviceEntryIconViewId)?.bottom?.toFloat() ?: 0f
-    }
-
-    override fun dozeTimeTick() {
-        // no-op
-    }
-
-    override fun setAlpha(alpha: Float) {
-        // no-op
-    }
-
-    override fun willHandleTouchWhileDozing(event: MotionEvent): Boolean {
-        return false
-    }
-}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index ec0f582..0e1eccc 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -55,7 +55,6 @@
 import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController;
 import com.android.systemui.statusbar.notification.AnimatableProperty;
 import com.android.systemui.statusbar.notification.PropertyAnimator;
-import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerAlwaysOnDisplayViewBinder;
 import com.android.systemui.statusbar.notification.stack.AnimationProperties;
 import com.android.systemui.statusbar.phone.NotificationIconContainer;
 import com.android.systemui.util.ViewController;
@@ -85,7 +84,6 @@
     private final DumpManager mDumpManager;
     private final ClockEventController mClockEventController;
     private final LogBuffer mLogBuffer;
-    private final NotificationIconContainerAlwaysOnDisplayViewBinder mNicViewBinder;
     private FrameLayout mSmallClockFrame; // top aligned clock
     private FrameLayout mLargeClockFrame; // centered clock
 
@@ -147,7 +145,6 @@
             ClockRegistry clockRegistry,
             KeyguardSliceViewController keyguardSliceViewController,
             LockscreenSmartspaceController smartspaceController,
-            NotificationIconContainerAlwaysOnDisplayViewBinder nicViewBinder,
             KeyguardUnlockAnimationController keyguardUnlockAnimationController,
             SecureSettings secureSettings,
             @Main DelayableExecutor uiExecutor,
@@ -163,7 +160,6 @@
         mClockRegistry = clockRegistry;
         mKeyguardSliceViewController = keyguardSliceViewController;
         mSmartspaceController = smartspaceController;
-        mNicViewBinder = nicViewBinder;
         mSecureSettings = secureSettings;
         mUiExecutor = uiExecutor;
         mBgExecutor = bgExecutor;
@@ -277,7 +273,6 @@
             hideSliceViewAndNotificationIconContainer();
             return;
         }
-        updateAodIcons();
         mStatusArea = mView.findViewById(R.id.keyguard_status_area);
 
         mBgExecutor.execute(() -> {
@@ -569,21 +564,6 @@
         return mLargeClockFrame.getVisibility() != View.VISIBLE;
     }
 
-    private void updateAodIcons() {
-        if (!MigrateClocksToBlueprint.isEnabled()) {
-            NotificationIconContainer nic = (NotificationIconContainer)
-                    mView.findViewById(
-                            com.android.systemui.res.R.id.left_aligned_notification_icon_container);
-            if (mAodIconsBindHandle != null) {
-                mAodIconsBindHandle.dispose();
-            }
-            if (nic != null) {
-                mAodIconsBindHandle = mNicViewBinder.bindWhileAttached(nic);
-                mAodIconContainer = nic;
-            }
-        }
-    }
-
     private void setClock(ClockController clock) {
         if (MigrateClocksToBlueprint.isEnabled()) {
             return;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index 36afe1e..63d4fe3 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -51,6 +51,7 @@
 import android.graphics.Bitmap;
 import android.graphics.BlendMode;
 import android.graphics.Canvas;
+import android.graphics.Color;
 import android.graphics.Rect;
 import android.graphics.Typeface;
 import android.graphics.drawable.BitmapDrawable;
@@ -97,6 +98,7 @@
 import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
 import com.android.settingslib.Utils;
 import com.android.settingslib.drawable.CircleFramedDrawable;
+import com.android.systemui.Flags;
 import com.android.systemui.Gefingerpoken;
 import com.android.systemui.classifier.FalsingA11yDelegate;
 import com.android.systemui.plugins.FalsingManager;
@@ -346,8 +348,7 @@
         setPadding(getPaddingLeft(), getPaddingTop() + getResources().getDimensionPixelSize(
                         R.dimen.keyguard_security_container_padding_top), getPaddingRight(),
                 getPaddingBottom());
-        setBackgroundColor(
-                getContext().getColor(com.android.internal.R.color.materialColorSurfaceDim));
+        reloadBackgroundColor();
     }
 
     void onResume(SecurityMode securityMode, boolean faceAuthEnabled) {
@@ -812,10 +813,20 @@
         mDisappearAnimRunning = false;
     }
 
+    private void reloadBackgroundColor() {
+        if (Flags.bouncerUiRevamp()) {
+            // Keep the background transparent, otherwise the background color looks like a box
+            // while scaling the bouncer for back animation or while transitioning to the bouncer.
+            setBackgroundColor(Color.TRANSPARENT);
+        } else {
+            setBackgroundColor(
+                    getContext().getColor(com.android.internal.R.color.materialColorSurfaceDim));
+        }
+    }
+
     void reloadColors() {
         mViewMode.reloadColors();
-        setBackgroundColor(getContext().getColor(
-                com.android.internal.R.color.materialColorSurfaceDim));
+        reloadBackgroundColor();
     }
 
     /** Handles density or font scale changes. */
diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java b/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java
index aed1c10..dccf53a 100644
--- a/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java
+++ b/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java
@@ -25,7 +25,9 @@
 import android.animation.ArgbEvaluator;
 import android.animation.ValueAnimator;
 import android.annotation.Nullable;
+import android.annotation.SuppressLint;
 import android.content.Context;
+import android.content.res.TypedArray;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.GradientDrawable;
 import android.view.ContextThemeWrapper;
@@ -122,7 +124,11 @@
 
         int[] customAttrs = {android.R.attr.colorControlNormal};
         ContextThemeWrapper ctw = new ContextThemeWrapper(context, mStyle);
-        mNormalBackgroundColor = ctw.getColor(NUM_PAD_BACKGROUND);
+        @SuppressLint("ResourceType") TypedArray a = ctw.obtainStyledAttributes(customAttrs);
+
+        mNormalBackgroundColor = a.getColor(0, context.getColor(NUM_PAD_BACKGROUND));
+
+        a.recycle();
 
         mPressedBackgroundColor = context.getColor(NUM_PAD_BACKGROUND_PRESSED);
         mTextColorPressed = context.getColor(NUM_PAD_PRESSED);
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.kt b/packages/SystemUI/src/com/android/systemui/FontStyles.kt
similarity index 62%
rename from packages/SystemUI/src/com/android/keyguard/LockIconViewController.kt
rename to packages/SystemUI/src/com/android/systemui/FontStyles.kt
index c5012b0..d8cd6c87 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/FontStyles.kt
@@ -14,22 +14,15 @@
  * limitations under the License.
  */
 
-package com.android.keyguard
+package com.android.systemui
 
-import android.view.MotionEvent
-import android.view.View
+/** String tokens for the different GSF font families. */
+object FontStyles {
 
-/** Controls the [LockIconView]. */
-interface LockIconViewController {
-    fun setLockIconView(lockIconView: View)
+    const val GSF_LABEL_MEDIUM = "gsf-label-medium"
+    const val GSF_LABEL_LARGE = "gsf-label-large"
 
-    fun getTop(): Float
+    const val GSF_BODY_MEDIUM = "gsf-body-medium"
 
-    fun getBottom(): Float
-
-    fun dozeTimeTick()
-
-    fun setAlpha(alpha: Float)
-
-    fun willHandleTouchWhileDozing(event: MotionEvent): Boolean
+    const val GSF_TITLE_SMALL_EMPHASIZED = "gsf-title-small-emphasized"
 }
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/OWNERS b/packages/SystemUI/src/com/android/systemui/accessibility/OWNERS
index 1f66c91..1ed8c06 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/OWNERS
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/OWNERS
@@ -1,3 +1,4 @@
 # Bug component: 44215
 
-include /core/java/android/view/accessibility/OWNERS
\ No newline at end of file
+include /core/java/android/view/accessibility/OWNERS
+jonesriley@google.com
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java
index 559e6f7..ffb5f3d 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java
@@ -20,6 +20,7 @@
 import static android.provider.Settings.Secure.ACCESSIBILITY_FLOATING_MENU_MIGRATION_TOOLTIP_PROMPT;
 import static android.provider.Settings.Secure.ACCESSIBILITY_FLOATING_MENU_OPACITY;
 import static android.provider.Settings.Secure.ACCESSIBILITY_FLOATING_MENU_SIZE;
+import static android.provider.Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES;
 
 import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.SOFTWARE;
 import static com.android.internal.accessibility.dialog.AccessibilityTargetHelper.getTargets;
@@ -42,6 +43,7 @@
 import android.text.TextUtils;
 import android.util.Log;
 import android.view.View;
+import android.view.accessibility.AccessibilityManager;
 
 import androidx.annotation.NonNull;
 
@@ -75,6 +77,9 @@
 
     private final Context mContext;
     private final Configuration mConfiguration;
+    private final AccessibilityManager mAccessibilityManager;
+    private final AccessibilityManager.AccessibilityServicesStateChangeListener
+            mA11yServicesStateChangeListener = manager -> onTargetFeaturesChanged();
     private final Handler mHandler = new Handler(Looper.getMainLooper());
     private final OnSettingsContentsChanged mSettingsContentsCallback;
     private final SecureSettings mSecureSettings;
@@ -142,9 +147,10 @@
         }
     };
 
-    MenuInfoRepository(Context context,
+    MenuInfoRepository(Context context, AccessibilityManager accessibilityManager,
             OnSettingsContentsChanged settingsContentsChanged, SecureSettings secureSettings) {
         mContext = context;
+        mAccessibilityManager = accessibilityManager;
         mConfiguration = new Configuration(context.getResources().getConfiguration());
         mSettingsContentsCallback = settingsContentsChanged;
         mSecureSettings = secureSettings;
@@ -238,6 +244,13 @@
                 mSecureSettings.getUriFor(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS),
                 /* notifyForDescendants */ false, mMenuTargetFeaturesContentObserver,
                 UserHandle.USER_CURRENT);
+        if (!com.android.systemui.Flags.floatingMenuNarrowTargetContentObserver()) {
+            mSecureSettings.registerContentObserverForUserSync(
+                    mSecureSettings.getUriFor(ENABLED_ACCESSIBILITY_SERVICES),
+                    /* notifyForDescendants */ false,
+                    mMenuTargetFeaturesContentObserver,
+                    UserHandle.USER_CURRENT);
+        }
         mSecureSettings.registerContentObserverForUserSync(
                 mSecureSettings.getUriFor(Settings.Secure.ACCESSIBILITY_FLOATING_MENU_SIZE),
                 /* notifyForDescendants */ false, mMenuSizeContentObserver,
@@ -251,6 +264,11 @@
                 /* notifyForDescendants */ false, mMenuFadeOutContentObserver,
                 UserHandle.USER_CURRENT);
         mContext.registerComponentCallbacks(mComponentCallbacks);
+
+        if (!com.android.systemui.Flags.floatingMenuNarrowTargetContentObserver()) {
+            mAccessibilityManager.addAccessibilityServicesStateChangeListener(
+                    mA11yServicesStateChangeListener);
+        }
     }
 
     void unregisterObserversAndCallbacks() {
@@ -258,6 +276,11 @@
         mContext.getContentResolver().unregisterContentObserver(mMenuSizeContentObserver);
         mContext.getContentResolver().unregisterContentObserver(mMenuFadeOutContentObserver);
         mContext.unregisterComponentCallbacks(mComponentCallbacks);
+
+        if (!com.android.systemui.Flags.floatingMenuNarrowTargetContentObserver()) {
+            mAccessibilityManager.removeAccessibilityServicesStateChangeListener(
+                    mA11yServicesStateChangeListener);
+        }
     }
 
     interface OnSettingsContentsChanged {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerController.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerController.java
index cfcaa4f..cb96e78 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerController.java
@@ -42,7 +42,8 @@
             NavigationModeController navigationModeController) {
         mWindowManager = viewCaptureAwareWindowManager;
 
-        MenuViewModel menuViewModel = new MenuViewModel(context, secureSettings);
+        MenuViewModel menuViewModel = new MenuViewModel(
+                context, accessibilityManager, secureSettings);
         MenuViewAppearance menuViewAppearance = new MenuViewAppearance(context, windowManager);
 
         mMenuViewLayer = new MenuViewLayer(context, windowManager, accessibilityManager,
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewModel.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewModel.java
index 46c407e..f924784 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewModel.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewModel.java
@@ -17,6 +17,7 @@
 package com.android.systemui.accessibility.floatingmenu;
 
 import android.content.Context;
+import android.view.accessibility.AccessibilityManager;
 
 import androidx.lifecycle.LiveData;
 import androidx.lifecycle.MutableLiveData;
@@ -42,9 +43,10 @@
     private final MutableLiveData<Position> mPercentagePositionData = new MutableLiveData<>();
     private final MenuInfoRepository mInfoRepository;
 
-    MenuViewModel(Context context, SecureSettings secureSettings) {
-        mInfoRepository = new MenuInfoRepository(context, /* settingsContentsChanged= */ this,
-                secureSettings);
+    MenuViewModel(Context context, AccessibilityManager accessibilityManager,
+            SecureSettings secureSettings) {
+        mInfoRepository = new MenuInfoRepository(context,
+                accessibilityManager, /* settingsContentsChanged= */ this, secureSettings);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/ambient/statusbar/ui/AmbientStatusBarView.java b/packages/SystemUI/src/com/android/systemui/ambient/statusbar/ui/AmbientStatusBarView.java
index 9e1b09c..081d2a0 100644
--- a/packages/SystemUI/src/com/android/systemui/ambient/statusbar/ui/AmbientStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/ambient/statusbar/ui/AmbientStatusBarView.java
@@ -75,8 +75,8 @@
     private ShadowInfo mAmbientShadowInfo;
     private int mDrawableSize;
     private int mDrawableInsetSize;
-    private static final float KEY_SHADOW_ALPHA = 0.35f;
-    private static final float AMBIENT_SHADOW_ALPHA = 0.4f;
+    private static final float KEY_SHADOW_ALPHA = 0.8f;
+    private static final float AMBIENT_SHADOW_ALPHA = 0.6f;
 
     public AmbientStatusBarView(Context context) {
         this(context, null);
diff --git a/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java
index 1176cb0..c170557 100644
--- a/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java
+++ b/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java
@@ -52,6 +52,7 @@
 
 import com.android.app.animation.Interpolators;
 import com.android.systemui.DualToneHandler;
+import com.android.systemui.FontStyles;
 import com.android.systemui.battery.unified.BatteryColors;
 import com.android.systemui.battery.unified.BatteryDrawableState;
 import com.android.systemui.battery.unified.BatteryLayersDrawable;
@@ -387,7 +388,8 @@
         float fontHeight = mBatteryPercentView.getPaint().getFontMetricsInt(null);
         mBatteryPercentView.setLineHeight(TypedValue.COMPLEX_UNIT_PX, fontHeight);
         if (gsfQuickSettings()) {
-            mBatteryPercentView.setTypeface(Typeface.create("gsf-label-large", Typeface.NORMAL));
+            mBatteryPercentView.setTypeface(
+                    Typeface.create(FontStyles.GSF_LABEL_LARGE, Typeface.NORMAL));
         }
         if (mTextColor != 0) mBatteryPercentView.setTextColor(mTextColor);
         addView(mBatteryPercentView, new LayoutParams(
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 db4b0f2..54c52b5 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
@@ -43,7 +43,7 @@
 import androidx.lifecycle.repeatOnLifecycle
 import com.airbnb.lottie.LottieAnimationView
 import com.airbnb.lottie.LottieCompositionFactory
-import com.android.systemui.Flags.bpIconA11y
+import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.systemui.biometrics.Utils.ellipsize
 import com.android.systemui.biometrics.shared.model.BiometricModalities
 import com.android.systemui.biometrics.shared.model.BiometricModality
@@ -63,7 +63,6 @@
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.first
 import kotlinx.coroutines.flow.map
-import com.android.app.tracing.coroutines.launchTraced as launch
 
 private const val TAG = "BiometricViewBinder"
 
@@ -327,31 +326,30 @@
 
                 // reuse the icon as a confirm button
                 launch {
-                    if (bpIconA11y()) {
-                        viewModel.isIconConfirmButton.collect { isButton ->
-                            if (isButton) {
-                                iconView.onTouchListener { _: View, event: MotionEvent ->
-                                    viewModel.onOverlayTouch(event)
-                                }
+                    viewModel.isIconConfirmButton.collect { isButton ->
+                        if (isButton && !accessibilityManager.isEnabled) {
+                            iconView.onTouchListener { _: View, event: MotionEvent ->
+                                viewModel.onOverlayTouch(event)
+                            }
+                        } else {
+                            iconView.setOnTouchListener(null)
+                        }
+                    }
+                }
+
+                launch {
+                    combine(viewModel.isIconConfirmButton, viewModel.isAuthenticated, ::Pair)
+                        .collect { (isIconConfirmButton, authState) ->
+                            // Only use the icon as a button for talkback when coex and pending
+                            // confirmation
+                            if (
+                                accessibilityManager.isEnabled &&
+                                    isIconConfirmButton &&
+                                    authState.isAuthenticated
+                            ) {
                                 iconView.setOnClickListener { viewModel.confirmAuthenticated() }
-                            } else {
-                                iconView.setOnTouchListener(null)
-                                iconView.setOnClickListener(null)
                             }
                         }
-                    } else {
-                        viewModel.isIconConfirmButton
-                            .map { isPending ->
-                                when {
-                                    isPending && modalities.hasFaceAndFingerprint ->
-                                        View.OnTouchListener { _: View, event: MotionEvent ->
-                                            viewModel.onOverlayTouch(event)
-                                        }
-                                    else -> null
-                                }
-                            }
-                            .collect { onTouch -> iconView.setOnTouchListener(onTouch) }
-                    }
                 }
 
                 // dismiss prompt when authenticated and confirmed
@@ -365,22 +363,8 @@
                             backgroundView.setOnClickListener(null)
                             backgroundView.importantForAccessibility =
                                 IMPORTANT_FOR_ACCESSIBILITY_NO
-
-                            // Allow icon to be used as confirmation button with udfps and a11y
-                            // enabled
-                            if (
-                                !bpIconA11y() &&
-                                    accessibilityManager.isTouchExplorationEnabled &&
-                                    modalities.hasUdfps
-                            ) {
-                                iconView.setOnClickListener { viewModel.confirmAuthenticated() }
-                            }
                         }
                         if (authState.isAuthenticatedAndConfirmed) {
-                            view.announceForAccessibility(
-                                view.resources.getString(R.string.biometric_dialog_authenticated)
-                            )
-
                             launch {
                                 delay(authState.delay)
                                 if (authState.isAuthenticatedAndExplicitlyConfirmed) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptIconViewModel.kt
index 574c40d..788c792 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptIconViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptIconViewModel.kt
@@ -43,7 +43,7 @@
     enum class AuthType {
         Fingerprint,
         Face,
-        Coex
+        Coex,
     }
 
     /**
@@ -53,7 +53,7 @@
     val activeAuthType: Flow<AuthType> =
         combine(
             promptViewModel.modalities.distinctUntilChanged(),
-            promptViewModel.faceMode.distinctUntilChanged()
+            promptViewModel.faceMode.distinctUntilChanged(),
         ) { modalities, faceMode ->
             if (modalities.hasFaceAndFingerprint && !faceMode) {
                 AuthType.Coex
@@ -102,7 +102,7 @@
                         promptSelectorInteractor.fingerprintSensorType,
                         promptViewModel.isAuthenticated,
                         promptViewModel.isAuthenticating,
-                        promptViewModel.showingError
+                        promptViewModel.showingError,
                     ) {
                         rotation: DisplayRotation,
                         isInRearDisplayMode: Boolean,
@@ -117,13 +117,13 @@
                                     isInRearDisplayMode,
                                     authState.isAuthenticated,
                                     isAuthenticating,
-                                    showingError
+                                    showingError,
                                 )
                             else ->
                                 getFingerprintIconViewAsset(
                                     authState.isAuthenticated,
                                     isAuthenticating,
-                                    showingError
+                                    showingError,
                                 )
                         }
                     }
@@ -132,7 +132,7 @@
                         promptViewModel.isAuthenticated.distinctUntilChanged(),
                         promptViewModel.isAuthenticating.distinctUntilChanged(),
                         promptViewModel.isPendingConfirmation.distinctUntilChanged(),
-                        promptViewModel.showingError.distinctUntilChanged()
+                        promptViewModel.showingError.distinctUntilChanged(),
                     ) {
                         authState: PromptAuthState,
                         isAuthenticating: Boolean,
@@ -142,7 +142,7 @@
                             authState,
                             isAuthenticating,
                             isPendingConfirmation,
-                            showingError
+                            showingError,
                         )
                     }
                 AuthType.Coex ->
@@ -170,14 +170,14 @@
                                     authState,
                                     isAuthenticating,
                                     isPendingConfirmation,
-                                    showingError
+                                    showingError,
                                 )
                             else ->
                                 getCoexIconViewAsset(
                                     authState,
                                     isAuthenticating,
                                     isPendingConfirmation,
-                                    showingError
+                                    showingError,
                                 )
                         }
                     }
@@ -187,7 +187,7 @@
     private fun getFingerprintIconViewAsset(
         isAuthenticated: Boolean,
         isAuthenticating: Boolean,
-        showingError: Boolean
+        showingError: Boolean,
     ): Int {
         return if (isAuthenticated) {
             if (_previousIconWasError.value) {
@@ -214,7 +214,7 @@
         isInRearDisplayMode: Boolean,
         isAuthenticated: Boolean,
         isAuthenticating: Boolean,
-        showingError: Boolean
+        showingError: Boolean,
     ): Int {
         return if (isAuthenticated) {
             if (_previousIconWasError.value) {
@@ -240,7 +240,7 @@
         authState: PromptAuthState,
         isAuthenticating: Boolean,
         isPendingConfirmation: Boolean,
-        showingError: Boolean
+        showingError: Boolean,
     ): Int {
         return if (authState.isAuthenticated && isPendingConfirmation) {
             R.raw.face_dialog_wink_from_dark
@@ -262,7 +262,7 @@
         authState: PromptAuthState,
         isAuthenticating: Boolean,
         isPendingConfirmation: Boolean,
-        showingError: Boolean
+        showingError: Boolean,
     ): Int {
         return if (authState.isAuthenticatedAndExplicitlyConfirmed) {
             R.raw.fingerprint_dialogue_unlocked_to_checkmark_success_lottie
@@ -298,7 +298,7 @@
         authState: PromptAuthState,
         isAuthenticating: Boolean,
         isPendingConfirmation: Boolean,
-        showingError: Boolean
+        showingError: Boolean,
     ): Int {
         return if (authState.isAuthenticatedAndExplicitlyConfirmed) {
             R.raw.biometricprompt_sfps_unlock_to_success
@@ -338,7 +338,7 @@
                         promptViewModel.isAuthenticated,
                         promptViewModel.isAuthenticating,
                         promptViewModel.isPendingConfirmation,
-                        promptViewModel.showingError
+                        promptViewModel.showingError,
                     ) {
                         sensorType: FingerprintSensorType,
                         authState: PromptAuthState,
@@ -350,7 +350,7 @@
                             authState.isAuthenticated,
                             isAuthenticating,
                             isPendingConfirmation,
-                            showingError
+                            showingError,
                         )
                     }
                 AuthType.Face ->
@@ -370,7 +370,7 @@
         isAuthenticated: Boolean,
         isAuthenticating: Boolean,
         isPendingConfirmation: Boolean,
-        showingError: Boolean
+        showingError: Boolean,
     ): Int =
         if (isPendingConfirmation) {
             when (sensorType) {
@@ -381,7 +381,7 @@
             when (sensorType) {
                 FingerprintSensorType.POWER_BUTTON ->
                     R.string.security_settings_sfps_enroll_find_sensor_message
-                else -> R.string.fingerprint_dialog_touch_sensor
+                else -> R.string.accessibility_fingerprint_label
             }
         } else if (showingError) {
             R.string.biometric_dialog_try_again
@@ -392,7 +392,7 @@
     private fun getFaceIconContentDescriptionId(
         authState: PromptAuthState,
         isAuthenticating: Boolean,
-        showingError: Boolean
+        showingError: Boolean,
     ): Int =
         if (authState.isAuthenticatedAndExplicitlyConfirmed) {
             R.string.biometric_dialog_face_icon_description_confirmed
@@ -415,7 +415,7 @@
                         promptSelectorInteractor.fingerprintSensorType,
                         promptViewModel.isAuthenticated,
                         promptViewModel.isAuthenticating,
-                        promptViewModel.showingError
+                        promptViewModel.showingError,
                     ) {
                         sensorType: FingerprintSensorType,
                         authState: PromptAuthState,
@@ -427,7 +427,7 @@
                                 shouldAnimateFingerprintIconView(
                                     authState.isAuthenticated,
                                     isAuthenticating,
-                                    showingError
+                                    showingError,
                                 )
                         }
                     }
@@ -435,7 +435,7 @@
                     combine(
                         promptViewModel.isAuthenticated,
                         promptViewModel.isAuthenticating,
-                        promptViewModel.showingError
+                        promptViewModel.showingError,
                     ) { authState: PromptAuthState, isAuthenticating: Boolean, showingError: Boolean
                         ->
                         isAuthenticating ||
@@ -463,7 +463,7 @@
                                     authState.isAuthenticated,
                                     isAuthenticating,
                                     isPendingConfirmation,
-                                    showingError
+                                    showingError,
                                 )
                         }
                     }
@@ -483,14 +483,14 @@
     private fun shouldAnimateFingerprintIconView(
         isAuthenticated: Boolean,
         isAuthenticating: Boolean,
-        showingError: Boolean
+        showingError: Boolean,
     ) = (isAuthenticating && _previousIconWasError.value) || isAuthenticated || showingError
 
     private fun shouldAnimateCoexIconView(
         isAuthenticated: Boolean,
         isAuthenticating: Boolean,
         isPendingConfirmation: Boolean,
-        showingError: Boolean
+        showingError: Boolean,
     ) =
         (isAuthenticating && _previousIconWasError.value) ||
             isPendingConfirmation ||
@@ -522,7 +522,7 @@
         listOf(
             R.raw.biometricprompt_sfps_fingerprint_authenticating,
             R.raw.biometricprompt_sfps_rear_display_fingerprint_authenticating,
-            R.raw.biometricprompt_sfps_rear_display_fingerprint_authenticating
+            R.raw.biometricprompt_sfps_rear_display_fingerprint_authenticating,
         )
 
     /** Called on configuration changes */
@@ -579,7 +579,7 @@
                 R.raw.fingerprint_dialogue_error_to_success_lottie,
                 R.raw.fingerprint_dialogue_fingerprint_to_success_lottie,
                 R.raw.fingerprint_dialogue_error_to_fingerprint_lottie,
-                R.raw.fingerprint_dialogue_fingerprint_to_error_lottie
+                R.raw.fingerprint_dialogue_fingerprint_to_error_lottie,
             )
         }
 
@@ -620,7 +620,7 @@
                 R.raw.fingerprint_dialogue_error_to_fingerprint_lottie,
                 R.raw.fingerprint_dialogue_error_to_success_lottie,
                 R.raw.fingerprint_dialogue_fingerprint_to_error_lottie,
-                R.raw.fingerprint_dialogue_fingerprint_to_success_lottie
+                R.raw.fingerprint_dialogue_fingerprint_to_success_lottie,
             )
         }
 
@@ -632,7 +632,7 @@
             R.raw.face_dialog_dark_to_error,
             R.raw.face_dialog_error_to_idle,
             R.raw.face_dialog_idle_static,
-            R.raw.face_dialog_authenticating
+            R.raw.face_dialog_authenticating,
         )
 
     private fun getSfpsAsset_fingerprintAuthenticating(isInRearDisplayMode: Boolean): Int =
@@ -644,7 +644,7 @@
 
     private fun getSfpsAsset_fingerprintToError(
         rotation: DisplayRotation,
-        isInRearDisplayMode: Boolean
+        isInRearDisplayMode: Boolean,
     ): Int =
         if (isInRearDisplayMode) {
             when (rotation) {
@@ -668,7 +668,7 @@
 
     private fun getSfpsAsset_errorToFingerprint(
         rotation: DisplayRotation,
-        isInRearDisplayMode: Boolean
+        isInRearDisplayMode: Boolean,
     ): Int =
         if (isInRearDisplayMode) {
             when (rotation) {
@@ -692,7 +692,7 @@
 
     private fun getSfpsAsset_fingerprintToUnlock(
         rotation: DisplayRotation,
-        isInRearDisplayMode: Boolean
+        isInRearDisplayMode: Boolean,
     ): Int =
         if (isInRearDisplayMode) {
             when (rotation) {
@@ -716,7 +716,7 @@
 
     private fun getSfpsAsset_fingerprintToSuccess(
         rotation: DisplayRotation,
-        isInRearDisplayMode: Boolean
+        isInRearDisplayMode: Boolean,
     ): Int =
         if (isInRearDisplayMode) {
             when (rotation) {
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingStartModule.kt b/packages/SystemUI/src/com/android/systemui/classifier/FalsingStartModule.kt
index a9f8f37..4246430 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingStartModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingStartModule.kt
@@ -27,5 +27,5 @@
     @Binds
     @IntoMap
     @ClassKey(FalsingCoreStartable::class)
-    fun bindFalsingCoreStartable(falsingCoreStartable: FalsingCoreStartable?): CoreStartable?
+    fun bindFalsingCoreStartable(falsingCoreStartable: FalsingCoreStartable): CoreStartable
 }
diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/data/repository/ConfigurationRepository.kt b/packages/SystemUI/src/com/android/systemui/common/ui/data/repository/ConfigurationRepository.kt
index 4d804d0..747a2a9 100644
--- a/packages/SystemUI/src/com/android/systemui/common/ui/data/repository/ConfigurationRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/ui/data/repository/ConfigurationRepository.kt
@@ -53,8 +53,12 @@
     val onConfigurationChange: Flow<Unit>
 
     val scaleForResolution: Flow<Float>
+
     val configurationValues: Flow<Configuration>
 
+    /** Emits the latest display this configuration controller has been moved to. */
+    val onMovedToDisplay: Flow<Int>
+
     fun getResolutionScale(): Float
 
     /** Convenience to context.resources.getDimensionPixelSize() */
@@ -117,6 +121,20 @@
         configurationController.addCallback(callback)
         awaitClose { configurationController.removeCallback(callback) }
     }
+    override val onMovedToDisplay: Flow<Int>
+        get() = conflatedCallbackFlow {
+            val callback =
+                object : ConfigurationController.ConfigurationListener {
+                    override fun onMovedToDisplay(
+                        newDisplayId: Int,
+                        newConfiguration: Configuration?,
+                    ) {
+                        trySend(newDisplayId)
+                    }
+                }
+            configurationController.addCallback(callback)
+            awaitClose { configurationController.removeCallback(callback) }
+        }
 
     override val scaleForResolution: StateFlow<Float> =
         onConfigurationChange
diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt b/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt
index 5644e6b..34679b0 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt
@@ -183,7 +183,12 @@
                     this@CommunalSceneStartable.isDreaming = isDreaming
                     if (scene.isCommunal() && isDreaming && timeoutJob == null) {
                         // If dreaming starts after timeout has expired, ex. if dream restarts under
-                        // the hub, just close the hub immediately.
+                        // the hub, wait for IS_ABLE_TO_DREAM_DELAY_MS and then close the hub. The
+                        // delay is necessary so the KeyguardInteractor.isAbleToDream flow passes
+                        // through that same amount of delay and publishes a new value which is then
+                        // picked up by the HomeSceneFamilyResolver such that the next call to
+                        // SceneInteractor.changeScene(Home) will resolve "Home" to "Dream".
+                        delay(KeyguardInteractor.IS_ABLE_TO_DREAM_DELAY_MS)
                         communalSceneInteractor.changeScene(
                             CommunalScenes.Blank,
                             "dream started after timeout",
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/binder/CommunalAppWidgetHostViewBinder.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/binder/CommunalAppWidgetHostViewBinder.kt
deleted file mode 100644
index 71bfe0c..0000000
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/binder/CommunalAppWidgetHostViewBinder.kt
+++ /dev/null
@@ -1,110 +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.communal.ui.binder
-
-import android.content.Context
-import android.os.Bundle
-import android.util.SizeF
-import android.view.View
-import android.view.ViewGroup
-import android.widget.FrameLayout
-import androidx.compose.ui.unit.IntSize
-import androidx.core.view.doOnLayout
-import com.android.app.tracing.coroutines.launchTraced as launch
-import com.android.systemui.Flags.communalWidgetResizing
-import com.android.systemui.common.ui.view.onLayoutChanged
-import com.android.systemui.communal.domain.model.CommunalContentModel
-import com.android.systemui.communal.util.WidgetViewFactory
-import com.android.systemui.util.kotlin.DisposableHandles
-import com.android.systemui.util.kotlin.toDp
-import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
-import kotlin.coroutines.CoroutineContext
-import kotlin.coroutines.resume
-import kotlin.coroutines.suspendCoroutine
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.DisposableHandle
-import kotlinx.coroutines.channels.awaitClose
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.flowOn
-
-object CommunalAppWidgetHostViewBinder {
-    private const val TAG = "CommunalAppWidgetHostViewBinder"
-
-    fun bind(
-        context: Context,
-        applicationScope: CoroutineScope,
-        mainContext: CoroutineContext,
-        backgroundContext: CoroutineContext,
-        container: FrameLayout,
-        model: CommunalContentModel.WidgetContent.Widget,
-        size: SizeF?,
-        factory: WidgetViewFactory,
-    ): DisposableHandle {
-        val disposables = DisposableHandles()
-
-        val loadingJob =
-            applicationScope.launch("$TAG#createWidgetView") {
-                val widget = factory.createWidget(context, model, size)
-                waitForLayout(container)
-                container.post { container.setView(widget) }
-                if (communalWidgetResizing()) {
-                    // Update the app widget size in the background.
-                    launch("$TAG#updateSize", backgroundContext) {
-                        container.sizeFlow().flowOn(mainContext).distinctUntilChanged().collect {
-                            (width, height) ->
-                            widget.updateAppWidgetSize(
-                                /* newOptions = */ Bundle(),
-                                /* minWidth = */ width,
-                                /* minHeight = */ height,
-                                /* maxWidth = */ width,
-                                /* maxHeight = */ height,
-                                /* ignorePadding = */ true,
-                            )
-                        }
-                    }
-                }
-            }
-
-        disposables += DisposableHandle { loadingJob.cancel() }
-        disposables += DisposableHandle { container.removeAllViews() }
-
-        return disposables
-    }
-
-    private suspend fun waitForLayout(container: FrameLayout) = suspendCoroutine { cont ->
-        container.doOnLayout { cont.resume(Unit) }
-    }
-}
-
-private fun ViewGroup.setView(view: View) {
-    if (view.parent == this) {
-        return
-    }
-    (view.parent as? ViewGroup)?.removeView(view)
-    addView(view)
-}
-
-private fun View.sizeAsDp(): IntSize = IntSize(width.toDp(context), height.toDp(context))
-
-private fun View.sizeFlow(): Flow<IntSize> = conflatedCallbackFlow {
-    if (isLaidOut && !isLayoutRequested) {
-        trySend(sizeAsDp())
-    }
-    val disposable = onLayoutChanged { trySend(sizeAsDp()) }
-    awaitClose { disposable.dispose() }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/sections/CommunalAppWidgetSection.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/sections/CommunalAppWidgetSection.kt
index 2e12bad..9f19562 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/sections/CommunalAppWidgetSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/sections/CommunalAppWidgetSection.kt
@@ -16,98 +16,132 @@
 
 package com.android.systemui.communal.ui.view.layout.sections
 
+import android.os.Bundle
 import android.util.SizeF
+import android.view.View
 import android.view.View.IMPORTANT_FOR_ACCESSIBILITY_AUTO
 import android.view.View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
-import android.widget.FrameLayout
+import android.view.accessibility.AccessibilityNodeInfo
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.getValue
+import androidx.compose.runtime.remember
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.viewinterop.AndroidView
-import androidx.lifecycle.compose.collectAsStateWithLifecycle
-import com.android.systemui.Flags.communalWidgetResizing
+import com.android.systemui.Flags.communalHubUseThreadPoolForWidgets
 import com.android.systemui.communal.domain.model.CommunalContentModel
-import com.android.systemui.communal.ui.binder.CommunalAppWidgetHostViewBinder
-import com.android.systemui.communal.ui.viewmodel.BaseCommunalViewModel
-import com.android.systemui.communal.util.WidgetViewFactory
-import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.communal.ui.viewmodel.CommunalAppWidgetViewModel
+import com.android.systemui.communal.widgets.CommunalAppWidgetHostView
+import com.android.systemui.communal.widgets.WidgetInteractionHandler
 import com.android.systemui.dagger.qualifiers.UiBackground
+import com.android.systemui.lifecycle.rememberViewModel
 import com.android.systemui.res.R
+import java.util.concurrent.Executor
+import java.util.concurrent.LinkedBlockingQueue
+import java.util.concurrent.ThreadPoolExecutor
+import java.util.concurrent.TimeUnit
 import javax.inject.Inject
-import kotlin.coroutines.CoroutineContext
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.DisposableHandle
 
 class CommunalAppWidgetSection
 @Inject
 constructor(
-    @Application private val applicationScope: CoroutineScope,
-    @Main private val mainContext: CoroutineContext,
-    @UiBackground private val backgroundContext: CoroutineContext,
-    private val factory: WidgetViewFactory,
+    @UiBackground private val uiBgExecutor: Executor,
+    private val interactionHandler: WidgetInteractionHandler,
+    private val viewModelFactory: CommunalAppWidgetViewModel.Factory,
 ) {
 
     private companion object {
-        val DISPOSABLE_TAG = R.id.communal_widget_disposable_tag
+        const val TAG = "CommunalAppWidgetSection"
+        val LISTENER_TAG = R.id.communal_widget_listener_tag
+
+        val poolSize by lazy { Runtime.getRuntime().availableProcessors().coerceAtLeast(2) }
+
+        /**
+         * This executor is used for widget inflation. Parameters match what launcher uses. See
+         * [com.android.launcher3.util.Executors.THREAD_POOL_EXECUTOR].
+         */
+        val widgetExecutor by lazy {
+            ThreadPoolExecutor(
+                /*corePoolSize*/ poolSize,
+                /*maxPoolSize*/ poolSize,
+                /*keepAlive*/ 1,
+                /*unit*/ TimeUnit.SECONDS,
+                /*workQueue*/ LinkedBlockingQueue(),
+            )
+        }
     }
 
     @Composable
     fun Widget(
-        viewModel: BaseCommunalViewModel,
+        isFocusable: Boolean,
+        openWidgetEditor: () -> Unit,
         model: CommunalContentModel.WidgetContent.Widget,
         size: SizeF,
         modifier: Modifier = Modifier,
     ) {
-        val isFocusable by viewModel.isFocusable.collectAsStateWithLifecycle(initialValue = false)
+        val viewModel = rememberViewModel("$TAG#viewModel") { viewModelFactory.create() }
+        val longClickLabel = stringResource(R.string.accessibility_action_label_edit_widgets)
+        val accessibilityDelegate =
+            remember(longClickLabel, openWidgetEditor) {
+                WidgetAccessibilityDelegate(longClickLabel, openWidgetEditor)
+            }
 
         AndroidView(
             factory = { context ->
-                FrameLayout(context).apply {
-                    layoutParams =
-                        FrameLayout.LayoutParams(
-                            FrameLayout.LayoutParams.MATCH_PARENT,
-                            FrameLayout.LayoutParams.MATCH_PARENT,
-                        )
-
-                    // Need to attach the disposable handle to the view here instead of storing
-                    // the state in the composable in order to properly support lazy lists. In a
-                    // lazy list, when the composable is no longer in view - it will exit
-                    // composition and any state inside the composable will be lost. However,
-                    // the View instance will be re-used. Therefore we can store data on the view
-                    // in order to preserve it.
-                    setTag(
-                        DISPOSABLE_TAG,
-                        CommunalAppWidgetHostViewBinder.bind(
-                            context = context,
-                            container = this,
-                            model = model,
-                            size = if (!communalWidgetResizing()) size else null,
-                            factory = factory,
-                            applicationScope = applicationScope,
-                            mainContext = mainContext,
-                            backgroundContext = backgroundContext,
-                        ),
-                    )
-
-                    accessibilityDelegate = viewModel.widgetAccessibilityDelegate
+                CommunalAppWidgetHostView(context, interactionHandler).apply {
+                    if (communalHubUseThreadPoolForWidgets()) {
+                        setExecutor(widgetExecutor)
+                    } else {
+                        setExecutor(uiBgExecutor)
+                    }
                 }
             },
-            update = { container ->
-                container.importantForAccessibility =
+            update = { view ->
+                view.accessibilityDelegate = accessibilityDelegate
+                view.importantForAccessibility =
                     if (isFocusable) {
                         IMPORTANT_FOR_ACCESSIBILITY_AUTO
                     } else {
                         IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
                     }
-            },
-            onRelease = { view ->
-                val disposable = (view.getTag(DISPOSABLE_TAG) as? DisposableHandle)
-                disposable?.dispose()
+                view.setAppWidget(model.appWidgetId, model.providerInfo)
+                // To avoid calling the expensive setListener method on every recomposition if
+                // the appWidgetId hasn't changed, we store the current appWidgetId of the view in
+                // a tag.
+                if ((view.getTag(LISTENER_TAG) as? Int) != model.appWidgetId) {
+                    viewModel.setListener(model.appWidgetId, view)
+                    view.setTag(LISTENER_TAG, model.appWidgetId)
+                }
+                viewModel.updateSize(size, view)
             },
             modifier = modifier,
             // For reusing composition in lazy lists.
             onReset = {},
         )
     }
+
+    private class WidgetAccessibilityDelegate(
+        private val longClickLabel: String,
+        private val longClickAction: () -> Unit,
+    ) : View.AccessibilityDelegate() {
+        override fun onInitializeAccessibilityNodeInfo(host: View, info: AccessibilityNodeInfo) {
+            super.onInitializeAccessibilityNodeInfo(host, info)
+            // Hint user to long press in order to enter edit mode
+            info.addAction(
+                AccessibilityNodeInfo.AccessibilityAction(
+                    AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK.id,
+                    longClickLabel.lowercase(),
+                )
+            )
+        }
+
+        override fun performAccessibilityAction(host: View, action: Int, args: Bundle?): Boolean {
+            when (action) {
+                AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK.id -> {
+                    longClickAction()
+                    return true
+                }
+            }
+            return super.performAccessibilityAction(host, action, args)
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
index 5ecf2e6b..099a859 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
@@ -19,7 +19,6 @@
 import android.appwidget.AppWidgetProviderInfo
 import android.content.ComponentName
 import android.os.UserHandle
-import android.view.View
 import com.android.compose.animation.scene.ObservableTransitionState
 import com.android.compose.animation.scene.SceneKey
 import com.android.compose.animation.scene.TransitionKey
@@ -80,9 +79,6 @@
      */
     val glanceableTouchAvailable: Flow<Boolean> = anyOf(not(isTouchConsumed), isNestedScrolling)
 
-    /** Accessibility delegate to be set on CommunalAppWidgetHostView. */
-    open val widgetAccessibilityDelegate: View.AccessibilityDelegate? = null
-
     /**
      * The up-to-date value of the grid scroll offset. persisted to interactor on
      * {@link #persistScrollPosition}
@@ -195,7 +191,7 @@
     open fun onDismissCtaTile() {}
 
     /** Called as the user starts dragging a widget to reorder. */
-    open fun onReorderWidgetStart() {}
+    open fun onReorderWidgetStart(draggingItemKey: String) {}
 
     /** Called as the user finishes dragging a widget to reorder. */
     open fun onReorderWidgetEnd() {}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalAppWidgetViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalAppWidgetViewModel.kt
new file mode 100644
index 0000000..6bafd14f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalAppWidgetViewModel.kt
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.ui.viewmodel
+
+import android.appwidget.AppWidgetHost.AppWidgetHostListener
+import android.appwidget.AppWidgetHostView
+import android.os.Bundle
+import android.util.SizeF
+import com.android.app.tracing.coroutines.coroutineScopeTraced
+import com.android.app.tracing.coroutines.withContextTraced
+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.GlanceableHubWidgetManager
+import com.android.systemui.dagger.qualifiers.UiBackground
+import com.android.systemui.lifecycle.ExclusiveActivatable
+import dagger.Lazy
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import kotlin.coroutines.CoroutineContext
+import kotlinx.coroutines.awaitCancellation
+import kotlinx.coroutines.channels.BufferOverflow
+import kotlinx.coroutines.channels.Channel
+import kotlinx.coroutines.flow.receiveAsFlow
+
+/** View model for showing a widget. */
+class CommunalAppWidgetViewModel
+@AssistedInject
+constructor(
+    @UiBackground private val backgroundContext: CoroutineContext,
+    private val appWidgetHostLazy: Lazy<CommunalAppWidgetHost>,
+    private val listenerDelegateFactory: AppWidgetHostListenerDelegate.Factory,
+    private val glanceableHubWidgetManagerLazy: Lazy<GlanceableHubWidgetManager>,
+    private val multiUserHelper: GlanceableHubMultiUserHelper,
+) : ExclusiveActivatable() {
+
+    private companion object {
+        const val TAG = "CommunalAppWidgetViewModel"
+        const val CHANNEL_CAPACITY = 10
+    }
+
+    @AssistedFactory
+    interface Factory {
+        fun create(): CommunalAppWidgetViewModel
+    }
+
+    private val requests =
+        Channel<Request>(capacity = CHANNEL_CAPACITY, onBufferOverflow = BufferOverflow.DROP_OLDEST)
+
+    fun setListener(appWidgetId: Int, listener: AppWidgetHostListener) {
+        requests.trySend(SetListener(appWidgetId, listener))
+    }
+
+    fun updateSize(size: SizeF, view: AppWidgetHostView) {
+        requests.trySend(UpdateSize(size, view))
+    }
+
+    override suspend fun onActivated(): Nothing {
+        coroutineScopeTraced("$TAG#onActivated") {
+            requests.receiveAsFlow().collect { request ->
+                when (request) {
+                    is SetListener -> handleSetListener(request.appWidgetId, request.listener)
+                    is UpdateSize -> handleUpdateSize(request.size, request.view)
+                }
+            }
+        }
+
+        awaitCancellation()
+    }
+
+    private suspend fun handleSetListener(appWidgetId: Int, listener: AppWidgetHostListener) =
+        withContextTraced("$TAG#setListenerInner", backgroundContext) {
+            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(appWidgetId, listenerDelegateFactory.create(listener))
+                }
+            } 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(appWidgetId, listenerDelegateFactory.create(listener))
+                }
+            }
+        }
+
+    private suspend fun handleUpdateSize(size: SizeF, view: AppWidgetHostView) =
+        withContextTraced("$TAG#updateSizeInner", backgroundContext) {
+            view.updateAppWidgetSize(
+                /* newOptions = */ Bundle(),
+                /* minWidth = */ size.width.toInt(),
+                /* minHeight = */ size.height.toInt(),
+                /* maxWidth = */ size.width.toInt(),
+                /* maxHeight = */ size.height.toInt(),
+                /* ignorePadding = */ true,
+            )
+        }
+}
+
+private sealed interface Request
+
+/**
+ * [Request] to call [CommunalAppWidgetHost.setListener] to tie this view to a particular widget.
+ * Since this is involves an IPC to system_server, the call is asynchronous and happens in the
+ * background.
+ */
+private data class SetListener(val appWidgetId: Int, val listener: AppWidgetHostListener) : Request
+
+/**
+ * [Request] to call [AppWidgetHostView.updateAppWidgetSize] to notify the widget provider of the
+ * new size. Since this is involves an IPC to system_server, the call is asynchronous and happens in
+ * the background.
+ */
+private data class UpdateSize(val size: SizeF, val view: AppWidgetHostView) : Request
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt
index 736ed5c..52bf000 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt
@@ -164,9 +164,8 @@
         )
     }
 
-    override fun onReorderWidgetStart() {
-        // Clear selection status
-        setSelectedKey(null)
+    override fun onReorderWidgetStart(draggingItemKey: String) {
+        setSelectedKey(draggingItemKey)
         _reorderingWidgets.value = true
         uiEventLogger.log(CommunalUiEvent.COMMUNAL_HUB_REORDER_WIDGET_START)
     }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalTutorialIndicatorViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalTutorialIndicatorViewModel.kt
index 63a4972..ce3a2be 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalTutorialIndicatorViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalTutorialIndicatorViewModel.kt
@@ -17,21 +17,17 @@
 package com.android.systemui.communal.ui.viewmodel
 
 import com.android.systemui.communal.domain.interactor.CommunalTutorialInteractor
-import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor
 import javax.inject.Inject
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.asStateFlow
-import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flowOf
 
 /** View model for communal tutorial indicator on keyguard */
 class CommunalTutorialIndicatorViewModel
 @Inject
-constructor(
-    private val communalTutorialInteractor: CommunalTutorialInteractor,
-    bottomAreaInteractor: KeyguardBottomAreaInteractor,
-) {
+constructor(private val communalTutorialInteractor: CommunalTutorialInteractor) {
     /**
      * An observable for whether the tutorial indicator view should be visible.
      *
@@ -46,5 +42,6 @@
     }
 
     /** An observable for the alpha level for the tutorial indicator. */
-    val alpha: Flow<Float> = bottomAreaInteractor.alpha.distinctUntilChanged()
+    // TODO("b/383587536") find replacement for keyguardBottomAreaInteractor alpha
+    val alpha: Flow<Float> = flowOf(0f)
 }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
index eb7420f..ddc4d1c 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
@@ -17,11 +17,8 @@
 package com.android.systemui.communal.ui.viewmodel
 
 import android.content.ComponentName
-import android.content.res.Resources
-import android.os.Bundle
-import android.view.View
-import android.view.accessibility.AccessibilityNodeInfo
 import com.android.app.tracing.coroutines.launchTraced as launch
+import com.android.systemui.Flags
 import com.android.systemui.communal.domain.interactor.CommunalInteractor
 import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
 import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
@@ -44,7 +41,6 @@
 import com.android.systemui.media.controls.ui.view.MediaHost
 import com.android.systemui.media.controls.ui.view.MediaHostState
 import com.android.systemui.media.dagger.MediaModule
-import com.android.systemui.res.R
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
 import com.android.systemui.statusbar.KeyguardIndicationController
@@ -84,7 +80,6 @@
     @Main val mainDispatcher: CoroutineDispatcher,
     @Application private val scope: CoroutineScope,
     @Background private val bgScope: CoroutineScope,
-    @Main private val resources: Resources,
     keyguardTransitionInteractor: KeyguardTransitionInteractor,
     keyguardInteractor: KeyguardInteractor,
     private val keyguardIndicationController: KeyguardIndicationController,
@@ -218,39 +213,6 @@
             }
             .distinctUntilChanged()
 
-    override val widgetAccessibilityDelegate =
-        object : View.AccessibilityDelegate() {
-            override fun onInitializeAccessibilityNodeInfo(
-                host: View,
-                info: AccessibilityNodeInfo,
-            ) {
-                super.onInitializeAccessibilityNodeInfo(host, info)
-                // Hint user to long press in order to enter edit mode
-                info.addAction(
-                    AccessibilityNodeInfo.AccessibilityAction(
-                        AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK.id,
-                        resources
-                            .getString(R.string.accessibility_action_label_edit_widgets)
-                            .lowercase(),
-                    )
-                )
-            }
-
-            override fun performAccessibilityAction(
-                host: View,
-                action: Int,
-                args: Bundle?,
-            ): Boolean {
-                when (action) {
-                    AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK.id -> {
-                        onOpenWidgetEditor()
-                        return true
-                    }
-                }
-                return super.performAccessibilityAction(host, action, args)
-            }
-        }
-
     private val _isEnableWidgetDialogShowing: MutableStateFlow<Boolean> = MutableStateFlow(false)
     val isEnableWidgetDialogShowing: Flow<Boolean> = _isEnableWidgetDialogShowing.asStateFlow()
 
@@ -293,6 +255,10 @@
     }
 
     override fun onLongClick() {
+        if (Flags.glanceableHubDirectEditMode()) {
+            onOpenWidgetEditor(false)
+            return
+        }
         setCurrentPopupType(PopupType.CustomizeWidgetButton)
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/communal/util/WidgetViewFactory.kt b/packages/SystemUI/src/com/android/systemui/communal/util/WidgetViewFactory.kt
deleted file mode 100644
index 50d86a2..0000000
--- a/packages/SystemUI/src/com/android/systemui/communal/util/WidgetViewFactory.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.systemui.communal.util
-
-import android.content.Context
-import android.os.Bundle
-import android.util.SizeF
-import com.android.app.tracing.coroutines.withContextTraced as withContext
-import com.android.systemui.Flags
-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 java.util.concurrent.LinkedBlockingQueue
-import java.util.concurrent.ThreadPoolExecutor
-import java.util.concurrent.TimeUnit
-import javax.inject.Inject
-import kotlin.coroutines.CoroutineContext
-
-/** Factory for creating [CommunalAppWidgetHostView] in a background thread. */
-class WidgetViewFactory
-@Inject
-constructor(
-    @UiBackground private val uiBgContext: CoroutineContext,
-    @UiBackground private val uiBgExecutor: Executor,
-    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,
-        model: CommunalContentModel.WidgetContent.Widget,
-        size: SizeF?,
-    ): CommunalAppWidgetHostView =
-        withContext("$TAG#createWidget", uiBgContext) {
-            val view =
-                CommunalAppWidgetHostView(context, interactionHandler).apply {
-                    if (Flags.communalHubUseThreadPoolForWidgets()) {
-                        setExecutor(widgetExecutor)
-                    } else {
-                        setExecutor(uiBgExecutor)
-                    }
-                    setAppWidget(model.appWidgetId, model.providerInfo)
-                }
-
-            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(),
-                    /* minWidth = */ size.width.toInt(),
-                    /* minHeight = */ size.height.toInt(),
-                    /* maxWidth = */ size.width.toInt(),
-                    /* maxHeight = */ size.height.toInt(),
-                    /* ignorePadding = */ true,
-                )
-            }
-            view
-        }
-
-    private companion object {
-        const val TAG = "WidgetViewFactory"
-
-        val poolSize = Runtime.getRuntime().availableProcessors().coerceAtLeast(2)
-
-        /**
-         * This executor is used for widget inflation. Parameters match what launcher uses. See
-         * [com.android.launcher3.util.Executors.THREAD_POOL_EXECUTOR].
-         */
-        val widgetExecutor =
-            ThreadPoolExecutor(
-                /*corePoolSize*/ poolSize,
-                /*maxPoolSize*/ poolSize,
-                /*keepAlive*/ 1,
-                /*unit*/ TimeUnit.SECONDS,
-                /*workQueue*/ LinkedBlockingQueue(),
-            )
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/AppWidgetHostListenerDelegate.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/AppWidgetHostListenerDelegate.kt
index f341621..7d80acd 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/widgets/AppWidgetHostListenerDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/AppWidgetHostListenerDelegate.kt
@@ -36,7 +36,7 @@
 ) : AppWidgetHostListener {
 
     @AssistedFactory
-    interface Factory {
+    fun interface Factory {
         fun create(listener: AppWidgetHostListener): AppWidgetHostListenerDelegate
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/complication/ComplicationHostViewController.java b/packages/SystemUI/src/com/android/systemui/complication/ComplicationHostViewController.java
index 8527dcb3..35592a5 100644
--- a/packages/SystemUI/src/com/android/systemui/complication/ComplicationHostViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/complication/ComplicationHostViewController.java
@@ -29,6 +29,7 @@
 
 import androidx.constraintlayout.widget.ConstraintLayout;
 import androidx.lifecycle.LifecycleOwner;
+import androidx.lifecycle.Observer;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.systemui.dreams.DreamOverlayStateController;
@@ -58,6 +59,14 @@
     private final LifecycleOwner mLifecycleOwner;
     private final ComplicationCollectionViewModel mComplicationCollectionViewModel;
     private final HashMap<ComplicationId, Complication.ViewHolder> mComplications = new HashMap<>();
+
+    private final Observer<Collection<ComplicationViewModel>> mComplicationViewModelObserver =
+            new Observer<>() {
+                @Override
+                public void onChanged(Collection<ComplicationViewModel> complicationViewModels) {
+                    updateComplications(complicationViewModels);
+                }
+            };
     @VisibleForTesting
     boolean mIsAnimationEnabled;
 
@@ -80,13 +89,6 @@
                 Settings.Global.ANIMATOR_DURATION_SCALE, 1.0f, UserHandle.USER_CURRENT) != 0.0f;
     }
 
-    @Override
-    protected void onInit() {
-        super.onInit();
-        mComplicationCollectionViewModel.getComplications().observe(mLifecycleOwner,
-                complicationViewModels -> updateComplications(complicationViewModels));
-    }
-
     /**
      * Returns the region in display space occupied by complications. Touches in this region
      * (composed of a collection of individual rectangular regions) should be directed to the
@@ -166,10 +168,14 @@
 
     @Override
     protected void onViewAttached() {
+        mComplicationCollectionViewModel.getComplications().observe(mLifecycleOwner,
+                mComplicationViewModelObserver);
     }
 
     @Override
     protected void onViewDetached() {
+        mComplicationCollectionViewModel.getComplications().removeObserver(
+                mComplicationViewModelObserver);
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
index 1fc5494..3050cba 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
@@ -61,6 +61,7 @@
 import com.android.systemui.screenshot.ReferenceScreenshotModule;
 import com.android.systemui.settings.MultiUserUtilsModule;
 import com.android.systemui.settings.UserTracker;
+import com.android.systemui.settings.brightness.dagger.BrightnessSliderModule;
 import com.android.systemui.shade.NotificationShadeWindowControllerImpl;
 import com.android.systemui.shade.ShadeModule;
 import com.android.systemui.startable.Dependencies;
@@ -124,6 +125,7 @@
         AccessibilityRepositoryModule.class,
         AospPolicyModule.class,
         BatterySaverModule.class,
+        BrightnessSliderModule.class,
         CentralSurfacesModule.class,
         ClipboardOverlayOverrideModule.class,
         CollapsedStatusBarFragmentStartableModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/DeviceEntryModule.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/DeviceEntryModule.kt
index 6c335e7..0ab9661 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/DeviceEntryModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/DeviceEntryModule.kt
@@ -16,18 +16,13 @@
 
 package com.android.systemui.deviceentry
 
-import com.android.keyguard.EmptyLockIconViewController
-import com.android.keyguard.LockIconViewController
 import com.android.systemui.CoreStartable
-import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.deviceentry.data.repository.DeviceEntryRepositoryModule
 import com.android.systemui.deviceentry.data.repository.FaceWakeUpTriggersConfigModule
 import com.android.systemui.deviceentry.domain.interactor.DeviceUnlockedInteractor
 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
 import dagger.Binds
-import dagger.Lazy
 import dagger.Module
-import dagger.Provides
 import dagger.multibindings.ClassKey
 import dagger.multibindings.IntoMap
 import dagger.multibindings.Multibinds
@@ -45,14 +40,4 @@
     abstract fun deviceUnlockedInteractorActivator(
         activator: DeviceUnlockedInteractor.Activator
     ): CoreStartable
-
-    companion object {
-        @Provides
-        @SysUISingleton
-        fun provideLockIconViewController(
-            emptyLockIconViewController: Lazy<EmptyLockIconViewController>
-        ): LockIconViewController {
-            return emptyLockIconViewController.get()
-        }
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
index ba57918..673ee6d 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
@@ -37,6 +37,7 @@
 import com.android.systemui.doze.dagger.DozeScope;
 import com.android.systemui.doze.dagger.WrappedService;
 import com.android.systemui.statusbar.phone.DozeParameters;
+import com.android.systemui.keyguard.domain.interactor.DozeInteractor;
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
 import com.android.systemui.util.wakelock.SettableWakeLock;
 import com.android.systemui.util.wakelock.WakeLock;
@@ -82,6 +83,7 @@
     private final DozeLog mDozeLog;
     private final DozeScreenBrightness mDozeScreenBrightness;
     private final SelectedUserInteractor mSelectedUserInteractor;
+    private final DozeInteractor mDozeInteractor;
 
     private int mPendingScreenState = Display.STATE_UNKNOWN;
     private SettableWakeLock mWakeLock;
@@ -97,6 +99,7 @@
             Provider<UdfpsController> udfpsControllerProvider,
             DozeLog dozeLog,
             DozeScreenBrightness dozeScreenBrightness,
+            DozeInteractor dozeInteractor,
             SelectedUserInteractor selectedUserInteractor) {
         mDozeService = service;
         mHandler = handler;
@@ -108,6 +111,7 @@
         mDozeLog = dozeLog;
         mDozeScreenBrightness = dozeScreenBrightness;
         mSelectedUserInteractor = selectedUserInteractor;
+        mDozeInteractor = dozeInteractor;
 
         updateUdfpsController();
         if (mUdfpsController == null) {
@@ -225,6 +229,7 @@
         if (screenState != Display.STATE_UNKNOWN) {
             if (DEBUG) Log.d(TAG, "setDozeScreenState(" + screenState + ")");
             mDozeService.setDozeScreenState(screenState);
+            mDozeInteractor.setDozeScreenState(screenState);
             if (screenState == Display.STATE_DOZE) {
                 // If we're entering doze, update the doze screen brightness. We might have been
                 // clamping it to the dim brightness during the screen off animation, and we should
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
index 91b44e7..e1ebf7c 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
@@ -20,6 +20,7 @@
 import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
 import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL;
 import static android.view.WindowManager.ScreenshotSource.SCREENSHOT_GLOBAL_ACTIONS;
 import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_2BUTTON;
 
@@ -120,6 +121,8 @@
 import com.android.systemui.colorextraction.SysuiColorExtractor;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.display.data.repository.DisplayWindowPropertiesRepository;
+import com.android.systemui.display.shared.model.DisplayWindowProperties;
 import com.android.systemui.globalactions.domain.interactor.GlobalActionsInteractor;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.GlobalActions.GlobalActionsManager;
@@ -127,6 +130,7 @@
 import com.android.systemui.scrim.ScrimDrawable;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.shade.ShadeController;
+import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround;
 import com.android.systemui.statusbar.NotificationShadeWindowController;
 import com.android.systemui.statusbar.VibratorHelper;
 import com.android.systemui.statusbar.phone.LightBarController;
@@ -149,6 +153,8 @@
 
 import javax.inject.Inject;
 
+import dagger.Lazy;
+
 /**
  * Helper to show the global actions dialog.  Each item is an {@link Action} that may show depending
  * on whether the keyguard is showing, and whether the device is provisioned.
@@ -194,6 +200,8 @@
     // See NotificationManagerService.LONG_DELAY
     private static final int TOAST_VISIBLE_TIME = 3500;
 
+    private static final int DIALOG_WINDOW_TYPE = TYPE_STATUS_BAR_SUB_PANEL;
+
     private final Context mContext;
     private final GlobalActionsManager mWindowManagerFuncs;
     private final AudioManager mAudioManager;
@@ -261,6 +269,7 @@
     private final DialogTransitionAnimator mDialogTransitionAnimator;
     private final UserLogoutInteractor mLogoutInteractor;
     private final GlobalActionsInteractor mInteractor;
+    private final Lazy<DisplayWindowPropertiesRepository> mDisplayWindowPropertiesRepositoryLazy;
 
     @VisibleForTesting
     public enum GlobalActionsEvent implements UiEventLogger.UiEventEnum {
@@ -376,7 +385,8 @@
             DialogTransitionAnimator dialogTransitionAnimator,
             SelectedUserInteractor selectedUserInteractor,
             UserLogoutInteractor logoutInteractor,
-            GlobalActionsInteractor interactor) {
+            GlobalActionsInteractor interactor,
+            Lazy<DisplayWindowPropertiesRepository> displayWindowPropertiesRepository) {
         mContext = context;
         mWindowManagerFuncs = windowManagerFuncs;
         mAudioManager = audioManager;
@@ -413,6 +423,7 @@
         mSelectedUserInteractor = selectedUserInteractor;
         mLogoutInteractor = logoutInteractor;
         mInteractor = interactor;
+        mDisplayWindowPropertiesRepositoryLazy = displayWindowPropertiesRepository;
 
         // receive broadcasts
         IntentFilter filter = new IntentFilter();
@@ -473,9 +484,10 @@
      * @param isDeviceProvisioned True if device is provisioned
      * @param expandable          The expandable from which we should animate the dialog when
      *                            showing it
+     * @param displayId           Display that should show the dialog
      */
     public void showOrHideDialog(boolean keyguardShowing, boolean isDeviceProvisioned,
-            @Nullable Expandable expandable) {
+            @Nullable Expandable expandable, int displayId) {
         mKeyguardShowing = keyguardShowing;
         mDeviceProvisioned = isDeviceProvisioned;
         if (mDialog != null && mDialog.isShowing()) {
@@ -487,7 +499,7 @@
             mDialog.dismiss();
             mDialog = null;
         } else {
-            handleShow(expandable);
+            handleShow(expandable, displayId);
         }
     }
 
@@ -507,8 +519,8 @@
         mHandler.sendEmptyMessage(MESSAGE_DISMISS);
     }
 
-    protected void handleShow(@Nullable Expandable expandable) {
-        mDialog = createDialog();
+    protected void handleShow(@Nullable Expandable expandable, int displayId) {
+        mDialog = createDialog(displayId);
         prepareDialog();
 
         WindowManager.LayoutParams attrs = mDialog.getWindow().getAttributes();
@@ -686,16 +698,44 @@
         mPowerAdapter = new MyPowerOptionsAdapter();
     }
 
+
     /**
      * Create the global actions dialog.
      *
      * @return A new dialog.
      */
     protected ActionsDialogLite createDialog() {
+        return createDialog(mContext.getDisplayId());
+    }
+
+    private Context getContextForDisplay(int displayId) {
+        if (!ShadeWindowGoesAround.isEnabled()) {
+            Log.e(TAG, "Asked for the displayId=" + displayId
+                    + " context but returning default display one as ShadeWindowGoesAround flag "
+                    + "is disabled.");
+            return mContext;
+        }
+        try {
+            DisplayWindowProperties properties = mDisplayWindowPropertiesRepositoryLazy.get().get(
+                    displayId,
+                    DIALOG_WINDOW_TYPE);
+            return properties.getContext();
+        } catch (Exception e) {
+            Log.e(TAG, "Couldn't get context for displayId=" + displayId);
+            return mContext;
+        }
+    }
+    /**
+     * Create the global actions dialog with a specific context.
+     *
+     * @return A new dialog.
+     */
+    protected ActionsDialogLite createDialog(int displayId) {
+        final Context context = getContextForDisplay(displayId);
         initDialogItems();
 
         ActionsDialogLite dialog = new ActionsDialogLite(
-                mContext,
+                context,
                 com.android.systemui.res.R.style.Theme_SystemUI_Dialog_GlobalActionsLite,
                 mAdapter,
                 mOverflowAdapter,
@@ -704,7 +744,7 @@
                 mLightBarController,
                 mKeyguardStateController,
                 mNotificationShadeWindowController,
-                mStatusBarWindowControllerStore.getDefaultDisplay(),
+                mStatusBarWindowControllerStore.forDisplay(context.getDisplayId()),
                 this::onRefresh,
                 mKeyguardShowing,
                 mPowerAdapter,
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
index c5027cc..a6255d0 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
@@ -63,7 +63,8 @@
     public void showGlobalActions(GlobalActionsManager manager) {
         if (mDisabled) return;
         mGlobalActionsDialog.showOrHideDialog(mKeyguardStateController.isShowing(),
-                mDeviceProvisionedController.isDeviceProvisioned(), null /* view */);
+                mDeviceProvisionedController.isDeviceProvisioned(), null /* view */,
+                mContext.getDisplayId());
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/ActionKeyTutorialScreen.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/ActionKeyTutorialScreen.kt
index 058e587..950a727 100644
--- a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/ActionKeyTutorialScreen.kt
+++ b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/ActionKeyTutorialScreen.kt
@@ -79,6 +79,9 @@
                 bodyResId = R.string.tutorial_action_key_guidance,
                 titleSuccessResId = R.string.tutorial_action_key_success_title,
                 bodySuccessResId = R.string.tutorial_action_key_success_body,
+                // error state for action key is not implemented yet so below should never appear
+                titleErrorResId = R.string.gesture_error_title,
+                bodyErrorResId = R.string.touchpad_action_key_error_body,
             ),
         animations = TutorialScreenConfig.Animations(educationResId = R.raw.action_key_edu),
     )
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 0c1bc83..c40adfe 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
@@ -45,18 +45,33 @@
 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.Error
 import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.Finished
+import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.InProgress
+import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.InProgressAfterError
+import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.NotStarted
 
 sealed interface TutorialActionState {
     data object NotStarted : TutorialActionState
 
     data class InProgress(
-        val progress: Float = 0f,
-        val startMarker: String? = null,
-        val endMarker: String? = null,
-    ) : TutorialActionState
+        override val progress: Float = 0f,
+        override val startMarker: String? = null,
+        override val endMarker: String? = null,
+    ) : TutorialActionState, Progress
 
     data class Finished(@RawRes val successAnimation: Int) : TutorialActionState
+
+    data object Error : TutorialActionState
+
+    data class InProgressAfterError(val inProgress: InProgress) :
+        TutorialActionState, Progress by inProgress
+}
+
+interface Progress {
+    val progress: Float
+    val startMarker: String?
+    val endMarker: String?
 }
 
 @Composable
@@ -133,10 +148,13 @@
     val focusRequester = remember { FocusRequester() }
     LaunchedEffect(Unit) { focusRequester.requestFocus() }
     val (titleTextId, bodyTextId) =
-        if (actionState is Finished) {
-            config.strings.titleSuccessResId to config.strings.bodySuccessResId
-        } else {
-            config.strings.titleResId to config.strings.bodyResId
+        when (actionState) {
+            is Finished -> config.strings.titleSuccessResId to config.strings.bodySuccessResId
+            Error,
+            is InProgressAfterError ->
+                config.strings.titleErrorResId to config.strings.bodyErrorResId
+            is NotStarted,
+            is InProgress -> config.strings.titleResId to config.strings.bodyResId
         }
     Column(verticalArrangement = Arrangement.Top, modifier = modifier) {
         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 ad18817..b0816ce 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
@@ -47,8 +47,10 @@
 import com.airbnb.lottie.compose.LottieDynamicProperties
 import com.airbnb.lottie.compose.animateLottieCompositionAsState
 import com.airbnb.lottie.compose.rememberLottieComposition
+import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.Error
 import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.Finished
 import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.InProgress
+import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.InProgressAfterError
 import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.NotStarted
 import com.android.systemui.res.R
 
@@ -72,16 +74,18 @@
             },
         ) { state ->
             when (state) {
-                NotStarted::class ->
+                NotStarted::class,
+                Error::class ->
                     EducationAnimation(
                         config.animations.educationResId,
                         config.colors.animationColors,
                     )
-                InProgress::class ->
+                InProgress::class,
+                InProgressAfterError::class ->
                     InProgressAnimation(
                         // actionState can be already of different class while this composable is
                         // transitioning to another one
-                        actionState as? InProgress,
+                        actionState as? Progress,
                         config.animations.educationResId,
                         config.colors.animationColors,
                     )
@@ -138,14 +142,14 @@
 
 @Composable
 private fun InProgressAnimation(
-    state: InProgress?,
+    state: Progress?,
     @RawRes inProgressAnimationId: Int,
     animationProperties: LottieDynamicProperties,
 ) {
     // Caching latest progress for when we're animating this view away and state is null.
     // Without this there's jumpcut in the animation while it's animating away.
     // state should never be null when composable appears, only when disappearing
-    val cached = remember { Ref<InProgress>() }
+    val cached = remember { Ref<Progress>() }
     cached.value = state ?: cached.value
     val progress = cached.value?.progress ?: 0f
 
diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/TutorialScreenConfig.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/TutorialScreenConfig.kt
index 60dfed3..2625991 100644
--- a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/TutorialScreenConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/TutorialScreenConfig.kt
@@ -38,6 +38,8 @@
         @StringRes val bodyResId: Int,
         @StringRes val titleSuccessResId: Int,
         @StringRes val bodySuccessResId: Int,
+        @StringRes val titleErrorResId: Int,
+        @StringRes val bodyErrorResId: Int,
     )
 
     data class Animations(@RawRes val educationResId: Int)
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/model/InternalShortcutModels.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/model/InternalShortcutModels.kt
index 3020e5d..b597136 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/model/InternalShortcutModels.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/model/InternalShortcutModels.kt
@@ -49,7 +49,7 @@
  * @param isCustomShortcut If Shortcut is user customized or system defined.
  */
 data class InternalKeyboardShortcutInfo(
-    val label: String,
+    val label: String = "",
     val keycode: Int,
     val modifiers: Int,
     val baseCharacter: Char = Char.MIN_VALUE,
@@ -60,4 +60,4 @@
 data class InternalGroupsSource(
     val groups: List<InternalKeyboardShortcutGroup>,
     val type: ShortcutCategoryType,
-)
\ No newline at end of file
+)
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/AppLaunchDataRepository.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/AppLaunchDataRepository.kt
new file mode 100644
index 0000000..b029b03
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/AppLaunchDataRepository.kt
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.keyboard.shortcut.data.repository
+
+import android.hardware.input.AppLaunchData
+import android.hardware.input.InputGestureData.KeyTrigger
+import android.hardware.input.InputManager
+import android.util.Log
+import android.view.InputDevice
+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.shared.model.ShortcutCommand
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutKey
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
+import javax.inject.Inject
+
+@SysUISingleton
+class AppLaunchDataRepository
+@Inject
+constructor(
+    private val inputManager: InputManager,
+    @Background private val backgroundScope: CoroutineScope,
+    private val shortcutCategoriesUtils: ShortcutCategoriesUtils,
+    inputDeviceRepository: ShortcutHelperInputDeviceRepository,
+) {
+
+    private val shortcutCommandToAppLaunchDataMap:
+        StateFlow<Map<ShortcutCommandKey, AppLaunchData>> =
+        inputDeviceRepository.activeInputDevice
+            .map { inputDevice ->
+                if (inputDevice == null) {
+                    emptyMap()
+                }
+                else{
+                    buildCommandToAppLaunchDataMap(inputDevice)
+                }
+            }
+            .stateIn(
+                scope = backgroundScope,
+                started = SharingStarted.Eagerly,
+                initialValue = mapOf(),
+            )
+
+    fun getAppLaunchDataForShortcutWithCommand(shortcutCommand: ShortcutCommand): AppLaunchData? {
+        val shortcutCommandAsKey = ShortcutCommandKey(shortcutCommand)
+        return shortcutCommandToAppLaunchDataMap.value[shortcutCommandAsKey]
+    }
+
+    private fun buildCommandToAppLaunchDataMap(inputDevice: InputDevice):
+            Map<ShortcutCommandKey, AppLaunchData> {
+        val commandToAppLaunchDataMap =
+            mutableMapOf<ShortcutCommandKey, AppLaunchData>()
+        val appLaunchInputGestures = inputManager.appLaunchBookmarks
+        appLaunchInputGestures.forEach { inputGesture ->
+            val keyGlyphMap =
+                if (shortcutHelperKeyGlyph()) {
+                    inputManager.getKeyGlyphMap(inputDevice.id)
+                } else null
+
+            val shortcutCommand =
+                shortcutCategoriesUtils.toShortcutCommand(
+                    keyGlyphMap,
+                    inputDevice.keyCharacterMap,
+                    inputGesture.trigger as KeyTrigger,
+                )
+
+            if (shortcutCommand != null) {
+                commandToAppLaunchDataMap[ShortcutCommandKey(shortcutCommand)] =
+                    inputGesture.action.appLaunchData()!!
+            } else {
+                Log.w(
+                    TAG,
+                    "could not get Shortcut Command. inputGesture: $inputGesture",
+                )
+            }
+        }
+
+        return commandToAppLaunchDataMap
+    }
+
+    private data class ShortcutCommandKey(val keys: List<ShortcutKey>) {
+        constructor(
+            shortcutCommand: ShortcutCommand
+        ) : this(shortcutCommand.keys.sortedBy { it.toString() })
+    }
+
+    private companion object {
+        private const val TAG = "AppLaunchDataRepository"
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepository.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepository.kt
index 8afec04..18ca877 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepository.kt
@@ -30,48 +30,37 @@
 import com.android.systemui.keyboard.shared.model.ShortcutCustomizationRequestResult
 import com.android.systemui.keyboard.shortcut.shared.model.KeyCombination
 import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategory
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType
 import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCustomizationRequestInfo
-import com.android.systemui.keyboard.shortcut.shared.model.ShortcutHelperState.Active
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCustomizationRequestInfo.SingleShortcutCustomization
 import com.android.systemui.keyboard.shortcut.shared.model.ShortcutKey
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.stateIn
-import kotlinx.coroutines.withContext
 import javax.inject.Inject
-import kotlin.coroutines.CoroutineContext
 
 @SysUISingleton
 class CustomShortcutCategoriesRepository
 @Inject
 constructor(
-    stateRepository: ShortcutHelperStateRepository,
+    inputDeviceRepository: ShortcutHelperInputDeviceRepository,
     @Background private val backgroundScope: CoroutineScope,
-    @Background private val bgCoroutineContext: CoroutineContext,
     private val shortcutCategoriesUtils: ShortcutCategoriesUtils,
     private val inputGestureDataAdapter: InputGestureDataAdapter,
     private val customInputGesturesRepository: CustomInputGesturesRepository,
-    private val inputManager: InputManager
+    private val inputManager: InputManager,
+    private val appLaunchDataRepository: AppLaunchDataRepository,
 ) : ShortcutCategoriesRepository {
 
     private val _selectedKeyCombination = MutableStateFlow<KeyCombination?>(null)
     private val _shortcutBeingCustomized = mutableStateOf<ShortcutCustomizationRequestInfo?>(null)
 
-    private val activeInputDevice =
-        stateRepository.state.map {
-            if (it is Active) {
-                withContext(bgCoroutineContext) { inputManager.getInputDevice(it.deviceId) }
-            } else {
-                null
-            }
-        }
-
     val pressedKeys =
         _selectedKeyCombination
-            .combine(activeInputDevice) { keyCombination, inputDevice ->
+            .combine(inputDeviceRepository.activeInputDevice) { keyCombination, inputDevice ->
                 if (inputDevice == null || keyCombination == null) {
                     return@combine emptyList()
                 } else {
@@ -105,8 +94,10 @@
             )
 
     override val categories: Flow<List<ShortcutCategory>> =
-        combine(activeInputDevice, customInputGesturesRepository.customInputGestures)
-        { inputDevice, inputGestures ->
+        combine(
+                inputDeviceRepository.activeInputDevice,
+                customInputGesturesRepository.customInputGestures,
+            ) { inputDevice, inputGestures ->
                 if (inputDevice == null) {
                     emptyList()
                 } else {
@@ -147,10 +138,10 @@
     fun buildInputGestureDataForShortcutBeingCustomized(): InputGestureData? {
         try {
             return Builder()
-                .addKeyGestureTypeFromShortcutLabel()
+                .addKeyGestureTypeForShortcutBeingCustomized()
                 .addTriggerFromSelectedKeyCombination()
+                .addAppLaunchDataFromShortcutBeingCustomized()
                 .build()
-            // TODO(b/379648200) add app launch data after dynamic label/icon mapping implementation
         } catch (e: IllegalArgumentException) {
             Log.w(TAG, "could not add custom shortcut: $e")
             return null
@@ -158,9 +149,10 @@
     }
 
     private fun retrieveInputGestureDataForShortcutBeingDeleted(): InputGestureData? {
-        val keyGestureType = getKeyGestureTypeFromShortcutBeingDeletedLabel()
-        return customInputGesturesRepository.retrieveCustomInputGestures()
-            .firstOrNull { it.action.keyGestureType() == keyGestureType }
+        val keyGestureType = getKeyGestureTypeForShortcutBeingCustomized()
+        return customInputGesturesRepository.retrieveCustomInputGestures().firstOrNull {
+            it.action.keyGestureType() == keyGestureType
+        }
     }
 
     suspend fun confirmAndSetShortcutCurrentlyBeingCustomized():
@@ -183,8 +175,8 @@
         return customInputGesturesRepository.resetAllCustomInputGestures()
     }
 
-    private fun Builder.addKeyGestureTypeFromShortcutLabel(): Builder {
-        val keyGestureType = getKeyGestureTypeFromShortcutBeingCustomizedLabel()
+    private fun Builder.addKeyGestureTypeForShortcutBeingCustomized(): Builder {
+        val keyGestureType = getKeyGestureTypeForShortcutBeingCustomized()
 
         if (keyGestureType == null) {
             Log.w(
@@ -193,31 +185,28 @@
             )
             return this
         }
-
         return setKeyGestureType(keyGestureType)
     }
 
-    @KeyGestureType
-    private fun getKeyGestureTypeFromShortcutBeingCustomizedLabel(): Int? {
+    private fun Builder.addAppLaunchDataFromShortcutBeingCustomized(): Builder {
         val shortcutBeingCustomized =
-            getShortcutBeingCustomized() as? ShortcutCustomizationRequestInfo.Add
+            (_shortcutBeingCustomized.value as? SingleShortcutCustomization) ?: return this
 
-        if (shortcutBeingCustomized == null) {
-            Log.w(
-                TAG,
-                "Requested key gesture type from label but shortcut being customized is null",
-            )
-            return null
+        if (shortcutBeingCustomized.categoryType != ShortcutCategoryType.AppCategories) {
+            return this
         }
 
-        return inputGestureDataAdapter
-            .getKeyGestureTypeFromShortcutLabel(shortcutBeingCustomized.label)
+        val defaultShortcutCommand = shortcutBeingCustomized.shortcutCommand
+
+        val appLaunchData =
+            appLaunchDataRepository.getAppLaunchDataForShortcutWithCommand(defaultShortcutCommand)
+
+        return if (appLaunchData == null) this else this.setAppLaunchData(appLaunchData)
     }
 
     @KeyGestureType
-    private fun getKeyGestureTypeFromShortcutBeingDeletedLabel(): Int? {
-        val shortcutBeingCustomized =
-            getShortcutBeingCustomized() as? ShortcutCustomizationRequestInfo.Delete
+    private fun getKeyGestureTypeForShortcutBeingCustomized(): Int? {
+        val shortcutBeingCustomized = getShortcutBeingCustomized() as? SingleShortcutCustomization
 
         if (shortcutBeingCustomized == null) {
             Log.w(
@@ -227,8 +216,10 @@
             return null
         }
 
-        return inputGestureDataAdapter
-            .getKeyGestureTypeFromShortcutLabel(shortcutBeingCustomized.label)
+        return inputGestureDataAdapter.getKeyGestureTypeForShortcut(
+            shortcutLabel = shortcutBeingCustomized.label,
+            shortcutCategoryType = shortcutBeingCustomized.categoryType,
+        )
     }
 
     private fun Builder.addTriggerFromSelectedKeyCombination(): Builder {
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/DefaultShortcutCategoriesRepository.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/DefaultShortcutCategoriesRepository.kt
index 5bb7cdd..db35d49 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/DefaultShortcutCategoriesRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/DefaultShortcutCategoriesRepository.kt
@@ -16,7 +16,6 @@
 
 package com.android.systemui.keyboard.shortcut.data.repository
 
-import android.hardware.input.InputManager
 import android.view.KeyboardShortcutGroup
 import android.view.KeyboardShortcutInfo
 import com.android.systemui.dagger.SysUISingleton
@@ -36,29 +35,24 @@
 import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.InputMethodEditor
 import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.MultiTasking
 import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.System
-import com.android.systemui.keyboard.shortcut.shared.model.ShortcutHelperState.Active
 import javax.inject.Inject
-import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.stateIn
-import kotlinx.coroutines.withContext
 
 @SysUISingleton
 class DefaultShortcutCategoriesRepository
 @Inject
 constructor(
     @Background private val backgroundScope: CoroutineScope,
-    @Background private val backgroundDispatcher: CoroutineDispatcher,
     @SystemShortcuts private val systemShortcutsSource: KeyboardShortcutGroupsSource,
     @MultitaskingShortcuts private val multitaskingShortcutsSource: KeyboardShortcutGroupsSource,
     @AppCategoriesShortcuts private val appCategoriesShortcutsSource: KeyboardShortcutGroupsSource,
     @InputShortcuts private val inputShortcutsSource: KeyboardShortcutGroupsSource,
     @CurrentAppShortcuts private val currentAppShortcutsSource: KeyboardShortcutGroupsSource,
-    private val inputManager: InputManager,
-    stateRepository: ShortcutHelperStateRepository,
+    inputDeviceRepository: ShortcutHelperInputDeviceRepository,
     shortcutCategoriesUtils: ShortcutCategoriesUtils,
 ) : ShortcutCategoriesRepository {
 
@@ -83,17 +77,8 @@
             ),
         )
 
-    private val activeInputDevice =
-        stateRepository.state.map {
-            if (it is Active) {
-                withContext(backgroundDispatcher) { inputManager.getInputDevice(it.deviceId) }
-            } else {
-                null
-            }
-        }
-
     override val categories: Flow<List<ShortcutCategory>> =
-        activeInputDevice
+        inputDeviceRepository.activeInputDevice
             .map { inputDevice ->
                 if (inputDevice == null) {
                     return@map emptyList()
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/InputGestureDataAdapter.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/InputGestureDataAdapter.kt
index df7101e..6e754a3 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/InputGestureDataAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/InputGestureDataAdapter.kt
@@ -48,49 +48,54 @@
 import com.android.systemui.settings.UserTracker
 import javax.inject.Inject
 
-
+/**
+ * Serves as a bridge for converting InputGestureData API Models to Shortcut Helper Data Layer
+ * Models and vice versa.
+ */
 class InputGestureDataAdapter
 @Inject
 constructor(
     private val userTracker: UserTracker,
     private val inputGestureMaps: InputGestureMaps,
-    private val context: Context
+    private val context: Context,
 ) {
     private val userContext: Context
         get() = userTracker.createCurrentUserContext(userTracker.userContext)
 
-    fun toInternalGroupSources(
-        inputGestures: List<InputGestureData>
-    ): List<InternalGroupsSource> {
+    fun toInternalGroupSources(inputGestures: List<InputGestureData>): List<InternalGroupsSource> {
         val ungroupedInternalGroupSources =
             inputGestures.mapNotNull { gestureData ->
                 val keyTrigger = gestureData.trigger as KeyTrigger
                 val keyGestureType = gestureData.action.keyGestureType()
                 val appLaunchData: AppLaunchData? = gestureData.action.appLaunchData()
                 fetchGroupLabelByGestureType(keyGestureType)?.let { groupLabel ->
-                    toInternalKeyboardShortcutInfo(
-                        keyGestureType,
-                        keyTrigger,
-                        appLaunchData
-                    )?.let { internalKeyboardShortcutInfo ->
-                        val group =
-                            InternalKeyboardShortcutGroup(
-                                label = groupLabel,
-                                items = listOf(internalKeyboardShortcutInfo),
-                            )
+                    toInternalKeyboardShortcutInfo(keyGestureType, keyTrigger, appLaunchData)
+                        ?.let { internalKeyboardShortcutInfo ->
+                            val group =
+                                InternalKeyboardShortcutGroup(
+                                    label = groupLabel,
+                                    items = listOf(internalKeyboardShortcutInfo),
+                                )
 
-                        fetchShortcutCategoryTypeByGestureType(keyGestureType)?.let {
-                            InternalGroupsSource(groups = listOf(group), type = it)
+                            fetchShortcutCategoryTypeByGestureType(keyGestureType)?.let {
+                                InternalGroupsSource(groups = listOf(group), type = it)
+                            }
                         }
-                    }
                 }
             }
 
         return ungroupedInternalGroupSources
     }
 
-    fun getKeyGestureTypeFromShortcutLabel(label: String): Int? {
-        return inputGestureMaps.shortcutLabelToKeyGestureTypeMap[label]
+    fun getKeyGestureTypeForShortcut(
+        shortcutLabel: String,
+        shortcutCategoryType: ShortcutCategoryType,
+    ): Int? {
+        if (shortcutCategoryType == ShortcutCategoryType.AppCategories) {
+            return KEY_GESTURE_TYPE_LAUNCH_APPLICATION
+        }
+        val result = inputGestureMaps.shortcutLabelToKeyGestureTypeMap[shortcutLabel]
+        return result
     }
 
     private fun toInternalKeyboardShortcutInfo(
@@ -104,16 +109,14 @@
                 keycode = keyTrigger.keycode,
                 modifiers = keyTrigger.modifierState,
                 isCustomShortcut = true,
-                icon = appLaunchData?.let { fetchShortcutIconByAppLaunchData(appLaunchData) }
+                icon = appLaunchData?.let { fetchShortcutIconByAppLaunchData(appLaunchData) },
             )
         }
         return null
     }
 
     @SuppressLint("QueryPermissionsNeeded")
-    private fun fetchShortcutIconByAppLaunchData(
-        appLaunchData: AppLaunchData
-    ): Icon? {
+    private fun fetchShortcutIconByAppLaunchData(appLaunchData: AppLaunchData): Icon? {
         val intent = fetchIntentFromAppLaunchData(appLaunchData) ?: return null
         val resolvedActivity = resolveSingleMatchingActivityFrom(intent)
 
@@ -132,7 +135,7 @@
 
     private fun fetchShortcutLabelByGestureType(
         @KeyGestureType keyGestureType: Int,
-        appLaunchData: AppLaunchData?
+        appLaunchData: AppLaunchData?,
     ): String? {
         inputGestureMaps.gestureToInternalKeyboardShortcutInfoLabelResIdMap[keyGestureType]?.let {
             return context.getString(it)
@@ -152,16 +155,14 @@
         return if (resolvedActivity == null) {
             getIntentCategoryLabel(intent.selector?.categories?.iterator()?.next())
         } else resolvedActivity.loadLabel(userContext.packageManager).toString()
-
     }
 
     @SuppressLint("QueryPermissionsNeeded")
     private fun resolveSingleMatchingActivityFrom(intent: Intent): ActivityInfo? {
         val packageManager = userContext.packageManager
-        val resolvedActivity = intent.resolveActivityInfo(
-            packageManager,
-            /* flags= */ MATCH_DEFAULT_ONLY
-        ) ?: return null
+        val resolvedActivity =
+            intent.resolveActivityInfo(packageManager, /* flags= */ MATCH_DEFAULT_ONLY)
+                ?: return null
 
         val matchesMultipleActivities =
             ResolverActivity::class.qualifiedName.equals(resolvedActivity.name)
@@ -172,22 +173,26 @@
     }
 
     private fun getIntentCategoryLabel(category: String?): String? {
-        val categoryLabelRes = when (category.toString()) {
-            Intent.CATEGORY_APP_BROWSER -> R.string.keyboard_shortcut_group_applications_browser
-            Intent.CATEGORY_APP_CONTACTS -> R.string.keyboard_shortcut_group_applications_contacts
-            Intent.CATEGORY_APP_EMAIL -> R.string.keyboard_shortcut_group_applications_email
-            Intent.CATEGORY_APP_CALENDAR -> R.string.keyboard_shortcut_group_applications_calendar
-            Intent.CATEGORY_APP_MAPS -> R.string.keyboard_shortcut_group_applications_maps
-            Intent.CATEGORY_APP_MUSIC -> R.string.keyboard_shortcut_group_applications_music
-            Intent.CATEGORY_APP_MESSAGING -> R.string.keyboard_shortcut_group_applications_sms
-            Intent.CATEGORY_APP_CALCULATOR -> R.string.keyboard_shortcut_group_applications_calculator
-            else -> {
-                Log.w(TAG, ("No label for app category $category"))
-                null
+        val categoryLabelRes =
+            when (category.toString()) {
+                Intent.CATEGORY_APP_BROWSER -> R.string.keyboard_shortcut_group_applications_browser
+                Intent.CATEGORY_APP_CONTACTS ->
+                    R.string.keyboard_shortcut_group_applications_contacts
+                Intent.CATEGORY_APP_EMAIL -> R.string.keyboard_shortcut_group_applications_email
+                Intent.CATEGORY_APP_CALENDAR ->
+                    R.string.keyboard_shortcut_group_applications_calendar
+                Intent.CATEGORY_APP_MAPS -> R.string.keyboard_shortcut_group_applications_maps
+                Intent.CATEGORY_APP_MUSIC -> R.string.keyboard_shortcut_group_applications_music
+                Intent.CATEGORY_APP_MESSAGING -> R.string.keyboard_shortcut_group_applications_sms
+                Intent.CATEGORY_APP_CALCULATOR ->
+                    R.string.keyboard_shortcut_group_applications_calculator
+                else -> {
+                    Log.w(TAG, ("No label for app category $category"))
+                    null
+                }
             }
-        }
 
-        return if (categoryLabelRes == null){
+        return if (categoryLabelRes == null) {
             return null
         } else {
             context.getString(categoryLabelRes)
@@ -196,41 +201,48 @@
 
     private fun fetchIntentFromAppLaunchData(appLaunchData: AppLaunchData): Intent? {
         return when (appLaunchData) {
-            is CategoryData -> Intent.makeMainSelectorActivity(
-                /* selectorAction= */ ACTION_MAIN,
-                /* selectorCategory= */ appLaunchData.category
-            )
+            is CategoryData ->
+                Intent.makeMainSelectorActivity(
+                    /* selectorAction= */ ACTION_MAIN,
+                    /* selectorCategory= */ appLaunchData.category,
+                )
 
             is RoleData -> getRoleLaunchIntent(appLaunchData.role)
-            is ComponentData -> resolveComponentNameIntent(
-                packageName = appLaunchData.packageName,
-                className = appLaunchData.className
-            )
+            is ComponentData ->
+                resolveComponentNameIntent(
+                    packageName = appLaunchData.packageName,
+                    className = appLaunchData.className,
+                )
 
             else -> null
         }
     }
 
     private fun resolveComponentNameIntent(packageName: String, className: String): Intent? {
-        buildIntentFromComponentName(ComponentName(packageName, className))?.let { return it }
-        buildIntentFromComponentName(ComponentName(
-            userContext.packageManager.canonicalToCurrentPackageNames(arrayOf(packageName))[0],
-            className
-        ))?.let { return it }
+        buildIntentFromComponentName(ComponentName(packageName, className))?.let {
+            return it
+        }
+        buildIntentFromComponentName(
+                ComponentName(
+                    userContext.packageManager
+                        .canonicalToCurrentPackageNames(arrayOf(packageName))[0],
+                    className,
+                )
+            )
+            ?.let {
+                return it
+            }
         return null
     }
 
     private fun buildIntentFromComponentName(componentName: ComponentName): Intent? {
-        try{
+        try {
             val flags =
                 MATCH_DIRECT_BOOT_UNAWARE or MATCH_DIRECT_BOOT_AWARE or MATCH_UNINSTALLED_PACKAGES
             // attempt to retrieve activity info to see if a NameNotFoundException is thrown.
             userContext.packageManager.getActivityInfo(componentName, flags)
         } catch (e: NameNotFoundException) {
-            Log.w(
-                TAG,
-                "Unable to find activity info for componentName: $componentName"
-            )
+            Log.w(TAG, "Unable to find activity info for componentName: $componentName")
             return null
         }
 
@@ -246,8 +258,9 @@
         val roleManager = userContext.getSystemService(RoleManager::class.java)!!
         if (roleManager.isRoleAvailable(role)) {
             roleManager.getDefaultApplication(role)?.let { rolePackage ->
-                packageManager.getLaunchIntentForPackage(rolePackage)?.let { return it }
-                    ?: Log.w(TAG, "No launch intent for role $role")
+                packageManager.getLaunchIntentForPackage(rolePackage)?.let {
+                    return it
+                } ?: Log.w(TAG, "No launch intent for role $role")
             } ?: Log.w(TAG, "No default application for role $role, user= ${userContext.user}")
         } else {
             Log.w(TAG, "Role $role is not available.")
@@ -264,4 +277,4 @@
     private companion object {
         private const val TAG = "InputGestureDataUtils"
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutCategoriesUtils.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutCategoriesUtils.kt
index 4a725ec..cf5460f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutCategoriesUtils.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutCategoriesUtils.kt
@@ -18,6 +18,7 @@
 
 import android.content.Context
 import android.graphics.drawable.Icon
+import android.hardware.input.InputGestureData.KeyTrigger
 import android.hardware.input.InputManager
 import android.hardware.input.KeyGlyphMap
 import android.util.Log
@@ -137,8 +138,7 @@
             label = shortcutInfo.label,
             icon = toShortcutIcon(keepIcon, shortcutInfo),
             commands = listOf(shortcutCommand),
-            isCustomizable =
-                shortcutHelperExclusions.isShortcutCustomizable(shortcutInfo.label),
+            isCustomizable = shortcutHelperExclusions.isShortcutCustomizable(shortcutInfo.label),
         )
     }
 
@@ -158,6 +158,22 @@
         return ShortcutIcon(packageName = icon.resPackage, resourceId = icon.resId)
     }
 
+    fun toShortcutCommand(
+        keyGlyphMap: KeyGlyphMap?,
+        keyCharacterMap: KeyCharacterMap,
+        keyTrigger: KeyTrigger,
+    ): ShortcutCommand? {
+        return toShortcutCommand(
+            keyGlyphMap = keyGlyphMap,
+            keyCharacterMap = keyCharacterMap,
+            info =
+                InternalKeyboardShortcutInfo(
+                    keycode = keyTrigger.keycode,
+                    modifiers = keyTrigger.modifierState,
+                ),
+        )
+    }
+
     private fun toShortcutCommand(
         keyGlyphMap: KeyGlyphMap?,
         keyCharacterMap: KeyCharacterMap,
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperInputDeviceRepository.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperInputDeviceRepository.kt
new file mode 100644
index 0000000..1361373
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperInputDeviceRepository.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.keyboard.shortcut.data.repository
+
+import android.hardware.input.InputManager
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutHelperState.Active
+import javax.inject.Inject
+import kotlin.coroutines.CoroutineContext
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.withContext
+
+class ShortcutHelperInputDeviceRepository
+@Inject
+constructor(
+    stateRepository: ShortcutHelperStateRepository,
+    @Background private val backgroundScope: CoroutineScope,
+    @Background private val bgCoroutineContext: CoroutineContext,
+    private val inputManager: InputManager,
+) {
+    val activeInputDevice =
+        stateRepository.state
+            .map {
+                if (it is Active) {
+                    withContext(bgCoroutineContext) { inputManager.getInputDevice(it.deviceId) }
+                } else {
+                    null
+                }
+            }
+            .stateIn(scope = backgroundScope, started = SharingStarted.Lazily, initialValue = null)
+}
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 c7e6b43..d8bad25 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
@@ -18,7 +18,10 @@
 
 import androidx.annotation.DrawableRes
 
-data class ShortcutCommand(val keys: List<ShortcutKey>, val isCustom: Boolean = false)
+data class ShortcutCommand(
+    val keys: List<ShortcutKey> = emptyList(),
+    val isCustom: Boolean = false,
+)
 
 class ShortcutCommandBuilder {
     private val keys = mutableListOf<ShortcutKey>()
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutCustomizationRequestInfo.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutCustomizationRequestInfo.kt
index 095de41..f183247 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutCustomizationRequestInfo.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutCustomizationRequestInfo.kt
@@ -17,17 +17,27 @@
 package com.android.systemui.keyboard.shortcut.shared.model
 
 sealed interface ShortcutCustomizationRequestInfo {
-    data class Add(
-        val label: String = "",
-        val categoryType: ShortcutCategoryType = ShortcutCategoryType.System,
-        val subCategoryLabel: String = "",
-    ) : ShortcutCustomizationRequestInfo
 
-    data class Delete(
-        val label: String = "",
-        val categoryType: ShortcutCategoryType = ShortcutCategoryType.System,
-        val subCategoryLabel: String = "",
-    ) : ShortcutCustomizationRequestInfo
+    sealed interface SingleShortcutCustomization: ShortcutCustomizationRequestInfo {
+        val label: String
+        val categoryType: ShortcutCategoryType
+        val subCategoryLabel: String
+        val shortcutCommand: ShortcutCommand
+
+        data class Add(
+            override val label: String = "",
+            override val categoryType: ShortcutCategoryType = ShortcutCategoryType.System,
+            override val subCategoryLabel: String = "",
+            override val shortcutCommand: ShortcutCommand = ShortcutCommand(),
+        ) : SingleShortcutCustomization
+
+        data class Delete(
+            override val label: String = "",
+            override val categoryType: ShortcutCategoryType = ShortcutCategoryType.System,
+            override val subCategoryLabel: String = "",
+            override val shortcutCommand: ShortcutCommand = ShortcutCommand(),
+        ) : SingleShortcutCustomization
+    }
 
     data object Reset : ShortcutCustomizationRequestInfo
 }
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 af6f0cb..aea583d 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
@@ -115,7 +115,6 @@
 import androidx.compose.ui.util.fastForEachIndexed
 import com.android.compose.modifiers.thenIf
 import com.android.compose.ui.graphics.painter.rememberDrawablePainter
-import com.android.systemui.keyboard.shortcut.shared.model.Shortcut as ShortcutModel
 import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType
 import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCommand
 import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCustomizationRequestInfo
@@ -127,6 +126,7 @@
 import com.android.systemui.keyboard.shortcut.ui.model.ShortcutsUiState
 import com.android.systemui.res.R
 import kotlinx.coroutines.delay
+import com.android.systemui.keyboard.shortcut.shared.model.Shortcut as ShortcutModel
 
 @Composable
 fun ShortcutHelper(
@@ -505,10 +505,10 @@
                 isCustomizing = isCustomizing and category.type.includeInCustomization,
                 onCustomizationRequested = { requestInfo ->
                     when (requestInfo) {
-                        is ShortcutCustomizationRequestInfo.Add ->
+                        is ShortcutCustomizationRequestInfo.SingleShortcutCustomization.Add ->
                             onCustomizationRequested(requestInfo.copy(categoryType = category.type))
 
-                        is ShortcutCustomizationRequestInfo.Delete ->
+                        is ShortcutCustomizationRequestInfo.SingleShortcutCustomization.Delete ->
                             onCustomizationRequested(requestInfo.copy(categoryType = category.type))
 
                         ShortcutCustomizationRequestInfo.Reset ->
@@ -568,12 +568,12 @@
                     isCustomizing = isCustomizing && shortcut.isCustomizable,
                     onCustomizationRequested = { requestInfo ->
                         when (requestInfo) {
-                            is ShortcutCustomizationRequestInfo.Add ->
+                            is ShortcutCustomizationRequestInfo.SingleShortcutCustomization.Add ->
                                 onCustomizationRequested(
                                     requestInfo.copy(subCategoryLabel = subCategory.label)
                                 )
 
-                            is ShortcutCustomizationRequestInfo.Delete ->
+                            is ShortcutCustomizationRequestInfo.SingleShortcutCustomization.Delete ->
                                 onCustomizationRequested(
                                     requestInfo.copy(subCategoryLabel = subCategory.label)
                                 )
@@ -644,12 +644,18 @@
             isCustomizing = isCustomizing,
             onAddShortcutRequested = {
                 onCustomizationRequested(
-                    ShortcutCustomizationRequestInfo.Add(label = shortcut.label)
+                    ShortcutCustomizationRequestInfo.SingleShortcutCustomization.Add(
+                        label = shortcut.label,
+                        shortcutCommand = shortcut.commands.first(),
+                    )
                 )
             },
             onDeleteShortcutRequested = {
                 onCustomizationRequested(
-                    ShortcutCustomizationRequestInfo.Delete(label = shortcut.label)
+                    ShortcutCustomizationRequestInfo.SingleShortcutCustomization.Delete(
+                        label = shortcut.label,
+                        shortcutCommand = shortcut.commands.first(),
+                    )
                 )
             },
         )
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModel.kt
index 92e2592..373eb25 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModel.kt
@@ -66,8 +66,9 @@
             }
 
     fun onShortcutCustomizationRequested(requestInfo: ShortcutCustomizationRequestInfo) {
+        shortcutCustomizationInteractor.onCustomizationRequested(requestInfo)
         when (requestInfo) {
-            is ShortcutCustomizationRequestInfo.Add -> {
+            is ShortcutCustomizationRequestInfo.SingleShortcutCustomization.Add -> {
                 _shortcutCustomizationUiState.value =
                     AddShortcutDialog(
                         shortcutLabel = requestInfo.label,
@@ -75,12 +76,10 @@
                             shortcutCustomizationInteractor.getDefaultCustomShortcutModifierKey(),
                         pressedKeys = emptyList(),
                     )
-                shortcutCustomizationInteractor.onCustomizationRequested(requestInfo)
             }
 
-            is ShortcutCustomizationRequestInfo.Delete -> {
+            is ShortcutCustomizationRequestInfo.SingleShortcutCustomization.Delete -> {
                 _shortcutCustomizationUiState.value = DeleteShortcutDialog
-                shortcutCustomizationInteractor.onCustomizationRequested(requestInfo)
             }
 
             ShortcutCustomizationRequestInfo.Reset -> {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardBottomAreaRefactor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardBottomAreaRefactor.kt
deleted file mode 100644
index 779b27b..0000000
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardBottomAreaRefactor.kt
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.keyguard
-
-import com.android.systemui.Flags
-import com.android.systemui.flags.FlagToken
-import com.android.systemui.flags.RefactorFlagUtils
-
-/** Helper for reading or using the keyguard bottom area refactor flag. */
-@Suppress("NOTHING_TO_INLINE")
-object KeyguardBottomAreaRefactor {
-    /** The aconfig flag name */
-    const val FLAG_NAME = Flags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR
-
-    /** A token used for dependency declaration */
-    val token: FlagToken
-        get() = FlagToken(FLAG_NAME, isEnabled)
-
-    /** Is the refactor enabled */
-    @JvmStatic
-    inline val isEnabled
-        get() = Flags.keyguardBottomAreaRefactor()
-
-    /**
-     * Called to ensure code is only run when the flag is enabled. This protects users from the
-     * unintended behaviors caused by accidentally running new logic, while also crashing on an eng
-     * build to ensure that the refactor author catches issues in testing.
-     */
-    @JvmStatic
-    inline fun isUnexpectedlyInLegacyMode() =
-        RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, FLAG_NAME)
-
-    /**
-     * Called to ensure code is only run when the flag is disabled. This will throw an exception if
-     * the flag is enabled to ensure that the refactor author catches issues in testing.
-     */
-    @JvmStatic
-    inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_NAME)
-}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndication.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndication.java
index a0b25b9..984541b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndication.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndication.java
@@ -27,7 +27,7 @@
  * Data class containing display information (message, icon, styling) for indication to show at
  * the bottom of the keyguard.
  *
- * See {@link com.android.systemui.statusbar.phone.KeyguardBottomAreaView}.
+ * See {@link com.android.systemui.keyguard.ui.view.KeyguardRootView}.
  */
 public class KeyguardIndication {
     @Nullable
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
index 5ec6d37..e8eb497 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
@@ -41,7 +41,6 @@
 import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
 import com.android.systemui.keyguard.shared.model.LockscreenSceneBlueprint
 import com.android.systemui.keyguard.ui.binder.KeyguardBlueprintViewBinder
-import com.android.systemui.keyguard.ui.binder.KeyguardIndicationAreaBinder
 import com.android.systemui.keyguard.ui.binder.KeyguardRootViewBinder
 import com.android.systemui.keyguard.ui.binder.LightRevealScrimViewBinder
 import com.android.systemui.keyguard.ui.composable.LockscreenContent
@@ -50,7 +49,6 @@
 import com.android.systemui.keyguard.ui.view.KeyguardRootView
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardBlueprintViewModel
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
-import com.android.systemui.keyguard.ui.viewmodel.KeyguardIndicationAreaViewModel
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel
 import com.android.systemui.keyguard.ui.viewmodel.LightRevealScrimViewModel
@@ -59,7 +57,6 @@
 import com.android.systemui.plugins.FalsingManager
 import com.android.systemui.res.R
 import com.android.systemui.scene.shared.flag.SceneContainerFlag
-import com.android.systemui.shade.NotificationShadeWindowView
 import com.android.systemui.shade.ShadeDisplayAware
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
 import com.android.systemui.statusbar.KeyguardIndicationController
@@ -86,9 +83,6 @@
 constructor(
     private val keyguardRootView: KeyguardRootView,
     private val keyguardRootViewModel: KeyguardRootViewModel,
-    private val keyguardIndicationAreaViewModel: KeyguardIndicationAreaViewModel,
-    private val notificationShadeWindowView: NotificationShadeWindowView,
-    private val indicationController: KeyguardIndicationController,
     private val screenOffAnimationController: ScreenOffAnimationController,
     private val occludingAppDeviceEntryMessageViewModel: OccludingAppDeviceEntryMessageViewModel,
     private val chipbarCoordinator: ChipbarCoordinator,
@@ -163,23 +157,6 @@
         }
     }
 
-    fun bindIndicationArea() {
-        indicationAreaHandle?.dispose()
-
-        if (!KeyguardBottomAreaRefactor.isEnabled) {
-            keyguardRootView.findViewById<View?>(R.id.keyguard_indication_area)?.let {
-                keyguardRootView.removeView(it)
-            }
-        }
-
-        indicationAreaHandle =
-            KeyguardIndicationAreaBinder.bind(
-                notificationShadeWindowView.requireViewById(R.id.keyguard_indication_area),
-                keyguardIndicationAreaViewModel,
-                indicationController,
-            )
-    }
-
     /** Initialize views so that corresponding controllers have a view set. */
     private fun initializeViews() {
         val indicationArea = KeyguardIndicationArea(context, null)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt b/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt
index a137d6c..5b28a3f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt
@@ -131,8 +131,18 @@
             Log.d(TAG, "ActivityTaskManagerService#keyguardGoingAway()")
             activityTaskManagerService.keyguardGoingAway(0)
             isKeyguardGoingAway = true
-        } else {
-            // Hide the surface by setting the lockscreen showing.
+        } else if (isLockscreenShowing == true) {
+            // Re-show the lockscreen if the surface was visible and we want to make it invisible,
+            // and the lockscreen is currently showing (this is the usual case of the going away
+            // animation). Re-showing the lockscreen will cancel the going away animation. If we
+            // want to hide the surface, but the lockscreen is not currently showing, do nothing and
+            // wait for lockscreenVisibility to emit if it's appropriate to show the lockscreen (it
+            // might be disabled/suppressed).
+            Log.d(
+                TAG,
+                "setLockscreenShown(true) because we're setting the surface invisible " +
+                    "and lockscreen is already showing.",
+            )
             setLockscreenShown(true)
         }
     }
@@ -153,6 +163,10 @@
         nonApps: Array<RemoteAnimationTarget>,
         finishedCallback: IRemoteAnimationFinishedCallback,
     ) {
+        // Make sure this is true - we set it true when requesting keyguardGoingAway, but there are
+        // cases where WM starts this transition on its own.
+        isKeyguardGoingAway = true
+
         // Ensure that we've started a dismiss keyguard transition. WindowManager can start the
         // going away animation on its own, if an activity launches and then requests dismissing the
         // keyguard. In this case, this is the first and only signal we'll receive to start
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 096439b..4370abf 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
@@ -62,6 +62,7 @@
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionBootInteractor;
 import com.android.systemui.keyguard.domain.interactor.StartKeyguardTransitionModule;
 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransitionModule;
+import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransitionModule;
 import com.android.systemui.keyguard.ui.view.AlternateBouncerWindowViewBinder;
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordancesCombinedViewModelModule;
 import com.android.systemui.log.SessionTracker;
@@ -112,6 +113,7 @@
         includes = {
             DeviceEntryIconTransitionModule.class,
             FalsingModule.class,
+            PrimaryBouncerTransitionModule.class,
             KeyguardDataQuickAffordanceModule.class,
             KeyguardQuickAffordancesCombinedViewModelModule.class,
             KeyguardRepositoryModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
index d3c17cc..ac04dd5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
@@ -75,12 +75,6 @@
      */
     val animateBottomAreaDozingTransitions: StateFlow<Boolean>
 
-    /**
-     * Observable for the current amount of alpha that should be used for rendering the bottom area.
-     * UI.
-     */
-    val bottomAreaAlpha: StateFlow<Float>
-
     val keyguardAlpha: StateFlow<Float>
 
     val panelAlpha: MutableStateFlow<Float>
@@ -283,9 +277,6 @@
     /** Sets whether the bottom area UI should animate the transition out of doze state. */
     fun setAnimateDozingTransitions(animate: Boolean)
 
-    /** Sets the current amount of alpha that should be used for rendering the bottom area. */
-    @Deprecated("Deprecated as part of b/278057014") fun setBottomAreaAlpha(alpha: Float)
-
     /** Sets the current amount of alpha that should be used for rendering the keyguard. */
     fun setKeyguardAlpha(alpha: Float)
 
@@ -392,9 +383,6 @@
     override val animateBottomAreaDozingTransitions =
         _animateBottomAreaDozingTransitions.asStateFlow()
 
-    private val _bottomAreaAlpha = MutableStateFlow(1f)
-    override val bottomAreaAlpha = _bottomAreaAlpha.asStateFlow()
-
     private val _keyguardAlpha = MutableStateFlow(1f)
     override val keyguardAlpha = _keyguardAlpha.asStateFlow()
 
@@ -675,10 +663,6 @@
         _animateBottomAreaDozingTransitions.value = animate
     }
 
-    override fun setBottomAreaAlpha(alpha: Float) {
-        _bottomAreaAlpha.value = alpha
-    }
-
     override fun setKeyguardAlpha(alpha: Float) {
         _keyguardAlpha.value = alpha
     }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DozeInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DozeInteractor.kt
index d04e4f1..7c8bca8d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DozeInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DozeInteractor.kt
@@ -17,8 +17,11 @@
 package com.android.systemui.keyguard.domain.interactor
 
 import android.graphics.Point
+import android.view.Display
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.data.repository.KeyguardRepository
+import com.android.systemui.power.data.repository.PowerRepository
+import com.android.systemui.power.shared.model.DozeScreenStateModel
 import com.android.systemui.scene.domain.interactor.SceneInteractor
 import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.scene.shared.model.Scenes
@@ -30,6 +33,7 @@
 @Inject
 constructor(
     private val keyguardRepository: KeyguardRepository,
+    private val powerRepository: PowerRepository,
     // TODO(b/336364825) Remove Lazy when SceneContainerFlag is released -
     // while the flag is off, creating this object too early results in a crash
     private val sceneInteractor: Lazy<SceneInteractor>,
@@ -41,6 +45,20 @@
         return sceneInteractor.get().currentScene.value == Scenes.Lockscreen
     }
 
+    fun setDozeScreenState(state: Int) {
+        powerRepository.dozeScreenState.value =
+            when (state) {
+                Display.STATE_UNKNOWN -> DozeScreenStateModel.UNKNOWN
+                Display.STATE_OFF -> DozeScreenStateModel.OFF
+                Display.STATE_ON -> DozeScreenStateModel.ON
+                Display.STATE_DOZE -> DozeScreenStateModel.DOZE
+                Display.STATE_DOZE_SUSPEND -> DozeScreenStateModel.DOZE_SUSPEND
+                Display.STATE_VR -> DozeScreenStateModel.VR
+                Display.STATE_ON_SUSPEND -> DozeScreenStateModel.ON_SUSPEND
+                else -> throw IllegalArgumentException("Invalid DozeScreenState: $state")
+            }
+    }
+
     fun setAodAvailable(value: Boolean) {
         keyguardRepository.setAodAvailable(value)
     }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBottomAreaInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBottomAreaInteractor.kt
deleted file mode 100644
index 53f2416..0000000
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBottomAreaInteractor.kt
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- *  Copyright (C) 2022 The Android Open Source Project
- *
- *  Licensed under the Apache License, Version 2.0 (the "License");
- *  you may not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *       http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS 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.common.shared.model.Position
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.data.repository.KeyguardRepository
-import javax.inject.Inject
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.asStateFlow
-
-/** Encapsulates business-logic specifically related to the keyguard bottom area. */
-@SysUISingleton
-class KeyguardBottomAreaInteractor
-@Inject
-constructor(
-    private val repository: KeyguardRepository,
-) {
-    /** Whether to animate the next doze mode transition. */
-    val animateDozingTransitions: Flow<Boolean> = repository.animateBottomAreaDozingTransitions
-    /** The amount of alpha for the UI components of the bottom area. */
-    val alpha: Flow<Float> = repository.bottomAreaAlpha
-    /** The position of the keyguard clock. */
-    private val _clockPosition = MutableStateFlow(Position(0, 0))
-    /** See [ClockSection] */
-    @Deprecated("with MigrateClocksToBlueprint.isEnabled")
-    val clockPosition: Flow<Position> = _clockPosition.asStateFlow()
-
-    fun setClockPosition(x: Int, y: Int) {
-        _clockPosition.value = Position(x, y)
-    }
-
-    fun setAlpha(alpha: Float) {
-        repository.setBottomAreaAlpha(alpha)
-    }
-
-    fun setAnimateDozingTransitions(animate: Boolean) {
-        repository.setAnimateDozingTransitions(animate)
-    }
-
-    /**
-     * Returns whether the keyguard bottom area should be constrained to the top of the lock icon
-     */
-    fun shouldConstrainToTopOfLockIcon(): Boolean = repository.isUdfpsSupported()
-}
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 26c286d..0d9474e 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
@@ -200,7 +200,10 @@
      * Dozing and dreaming have overlapping events. If the doze state remains in FINISH, it means
      * that doze mode is not running and DREAMING is ok to commence.
      *
-     * Allow a brief moment to prevent rapidly oscillating between true/false signals.
+     * Allow a brief moment to prevent rapidly oscillating between true/false signals. The amount of
+     * time is [IS_ABLE_TO_DREAM_DELAY_MS] - consumers should consider waiting for that long before
+     * examining the value of this flow, to let other consumers have enough time to also see that
+     * same new value.
      */
     val isAbleToDream: Flow<Boolean> =
         dozeTransitionModel
@@ -212,7 +215,7 @@
                     // do not immediately process any dreaming information when exiting AOD. It
                     // should actually be quite strange to leave AOD and then go straight to
                     // DREAMING so this should be fine.
-                    delay(500L)
+                    delay(IS_ABLE_TO_DREAM_DELAY_MS)
                     isDreaming
                         .sample(powerInteractor.isAwake) { isDreaming, isAwake ->
                             isDreaming && isAwake
@@ -550,5 +553,11 @@
 
     companion object {
         private const val TAG = "KeyguardInteractor"
+        /**
+         * Amount of time that [KeyguardInteractor.isAbleToDream] is delayed; consumers of that flow
+         * should consider waiting this amount of time before check the value of this flow, to let
+         * other consumers have enough time to see the new value.
+         */
+        const val IS_ABLE_TO_DREAM_DELAY_MS = 500L
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractor.kt
index 8641dfa..a133f06 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractor.kt
@@ -90,6 +90,7 @@
     private val selectedUserInteractor: SelectedUserInteractor,
     keyguardEnabledInteractor: KeyguardEnabledInteractor,
     keyguardServiceLockNowInteractor: KeyguardServiceLockNowInteractor,
+    keyguardInteractor: KeyguardInteractor,
 ) {
 
     /**
@@ -106,13 +107,18 @@
             .onStart { emit(false) }
 
     /**
-     * Whether we can wake from AOD/DOZING directly to GONE, bypassing LOCKSCREEN/BOUNCER states.
+     * Whether we can wake from AOD/DOZING or DREAMING directly to GONE, bypassing
+     * LOCKSCREEN/BOUNCER states.
      *
      * This is possible in the following cases:
      * - Keyguard is disabled, either from an app request or from security being set to "None".
      * - Keyguard is suppressed, via adb locksettings.
      * - We're wake and unlocking (fingerprint auth occurred while asleep).
      * - We're allowed to ignore auth and return to GONE, due to timeouts not elapsing.
+     * - We're DREAMING and dismissible.
+     * - We're already GONE. Technically you're already awake when GONE, but this makes it easier to
+     *   reason about this state (for example, if canWakeDirectlyToGone, don't tell WM to pause the
+     *   top activity - something you should never do while GONE as well).
      */
     val canWakeDirectlyToGone =
         combine(
@@ -120,14 +126,19 @@
                 shouldSuppressKeyguard,
                 repository.biometricUnlockState,
                 repository.canIgnoreAuthAndReturnToGone,
+                transitionInteractor.currentKeyguardState,
             ) {
                 keyguardEnabled,
                 shouldSuppressKeyguard,
                 biometricUnlockState,
-                canIgnoreAuthAndReturnToGone ->
+                canIgnoreAuthAndReturnToGone,
+                currentState ->
                 (!keyguardEnabled || shouldSuppressKeyguard) ||
                     BiometricUnlockMode.isWakeAndUnlock(biometricUnlockState.mode) ||
-                    canIgnoreAuthAndReturnToGone
+                    canIgnoreAuthAndReturnToGone ||
+                    (currentState == KeyguardState.DREAMING &&
+                        keyguardInteractor.isKeyguardDismissible.value) ||
+                    currentState == KeyguardState.GONE
             }
             .distinctUntilChanged()
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
index 3b99bb4..184f302 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
@@ -34,7 +34,7 @@
 import com.android.systemui.scene.shared.model.Overlays
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.statusbar.notification.domain.interactor.NotificationLaunchAnimationInteractor
-import com.android.systemui.util.kotlin.Utils.Companion.toTriple
+import com.android.systemui.util.kotlin.Utils.Companion.toQuad
 import com.android.systemui.util.kotlin.sample
 import com.android.systemui.utils.coroutines.flow.flatMapLatestConflated
 import dagger.Lazy
@@ -240,10 +240,11 @@
         combine(
                 transitionInteractor.currentKeyguardState,
                 wakeToGoneInteractor.canWakeDirectlyToGone,
-                ::Pair,
+                surfaceBehindVisibility,
+                ::Triple,
             )
-            .sample(transitionInteractor.startedStepWithPrecedingStep, ::toTriple)
-            .map { (currentState, canWakeDirectlyToGone, startedWithPrev) ->
+            .sample(transitionInteractor.startedStepWithPrecedingStep, ::toQuad)
+            .map { (currentState, canWakeDirectlyToGone, surfaceBehindVis, startedWithPrev) ->
                 val startedFromStep = startedWithPrev.previousValue
                 val startedStep = startedWithPrev.newValue
                 val returningToGoneAfterCancellation =
@@ -296,6 +297,11 @@
                     // we should simply tell WM that the lockscreen is no longer visible, and
                     // *not* play the going away animation or related animations.
                     false
+                } else if (!surfaceBehindVis) {
+                    // If the surface behind is not visible, then the lockscreen has to be visible
+                    // since there's nothing to show. The surface behind will never be invisible if
+                    // the lockscreen is disabled or suppressed.
+                    true
                 } else {
                     currentState != KeyguardState.GONE
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt
index 5bad016..261c130 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt
@@ -24,7 +24,6 @@
 import androidx.lifecycle.repeatOnLifecycle
 import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.systemui.customization.R as customR
-import com.android.systemui.keyguard.KeyguardBottomAreaRefactor
 import com.android.systemui.keyguard.shared.model.KeyguardBlueprint
 import com.android.systemui.keyguard.ui.view.layout.blueprints.transitions.BaseBlueprintTransition
 import com.android.systemui.keyguard.ui.view.layout.blueprints.transitions.IntraBlueprintTransition
@@ -51,26 +50,11 @@
                         (prevBlueprint, blueprint) ->
                         val config = Config.DEFAULT
                         val transition =
-                            if (
-                                !KeyguardBottomAreaRefactor.isEnabled &&
-                                    prevBlueprint != null &&
-                                    prevBlueprint != blueprint
-                            ) {
-                                BaseBlueprintTransition(clockViewModel)
-                                    .addTransition(
-                                        IntraBlueprintTransition(
-                                            config,
-                                            clockViewModel,
-                                            smartspaceViewModel,
-                                        )
-                                    )
-                            } else {
-                                IntraBlueprintTransition(
-                                    config,
-                                    clockViewModel,
-                                    smartspaceViewModel,
-                                )
-                            }
+                            IntraBlueprintTransition(
+                                config,
+                                clockViewModel,
+                                smartspaceViewModel,
+                            )
 
                         viewModel.runTransition(constraintLayout, transition, config) {
                             // Replace sections from the previous blueprint with the new ones
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
deleted file mode 100644
index c59fe53..0000000
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
+++ /dev/null
@@ -1,586 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.keyguard.ui.binder
-
-import android.annotation.SuppressLint
-import android.content.res.ColorStateList
-import android.graphics.Rect
-import android.graphics.drawable.Animatable2
-import android.util.Size
-import android.view.View
-import android.view.ViewGroup
-import android.view.ViewGroup.MarginLayoutParams
-import android.view.WindowInsets
-import android.widget.ImageView
-import androidx.core.animation.CycleInterpolator
-import androidx.core.animation.ObjectAnimator
-import androidx.core.view.isInvisible
-import androidx.core.view.isVisible
-import androidx.core.view.marginLeft
-import androidx.core.view.marginRight
-import androidx.core.view.marginTop
-import androidx.core.view.updateLayoutParams
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.repeatOnLifecycle
-import com.android.app.animation.Interpolators
-import com.android.app.tracing.coroutines.launchTraced as launch
-import com.android.systemui.animation.ActivityTransitionAnimator
-import com.android.systemui.animation.Expandable
-import com.android.systemui.animation.view.LaunchableLinearLayout
-import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.common.ui.binder.IconViewBinder
-import com.android.systemui.common.ui.binder.TextViewBinder
-import com.android.systemui.keyguard.ui.viewmodel.KeyguardBottomAreaViewModel
-import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordanceViewModel
-import com.android.systemui.keyguard.util.WallpaperPickerIntentUtils
-import com.android.systemui.keyguard.util.WallpaperPickerIntentUtils.LAUNCH_SOURCE_KEYGUARD
-import com.android.systemui.lifecycle.repeatWhenAttached
-import com.android.systemui.plugins.ActivityStarter
-import com.android.systemui.plugins.FalsingManager
-import com.android.systemui.res.R
-import com.android.systemui.statusbar.VibratorHelper
-import com.android.systemui.util.doOnEnd
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.filter
-import kotlinx.coroutines.flow.flatMapLatest
-import kotlinx.coroutines.flow.map
-
-/**
- * Binds a keyguard bottom area view to its view-model.
- *
- * To use this properly, users should maintain a one-to-one relationship between the [View] and the
- * view-binding, binding each view only once. It is okay and expected for the same instance of the
- * view-model to be reused for multiple view/view-binder bindings.
- */
-@OptIn(ExperimentalCoroutinesApi::class)
-@Deprecated("Deprecated as part of b/278057014")
-object KeyguardBottomAreaViewBinder {
-
-    private const val EXIT_DOZE_BUTTON_REVEAL_ANIMATION_DURATION_MS = 250L
-    private const val SCALE_SELECTED_BUTTON = 1.23f
-    private const val DIM_ALPHA = 0.3f
-    private const val TAG = "KeyguardBottomAreaViewBinder"
-
-    /**
-     * Defines interface for an object that acts as the binding between the view and its view-model.
-     *
-     * Users of the [KeyguardBottomAreaViewBinder] class should use this to control the binder after
-     * it is bound.
-     */
-    // If updated, be sure to update [KeyguardQuickAffordanceViewBinder.kt]
-    @Deprecated("Deprecated as part of b/278057014")
-    interface Binding {
-        /** Notifies that device configuration has changed. */
-        fun onConfigurationChanged()
-
-        /**
-         * Returns whether the keyguard bottom area should be constrained to the top of the lock
-         * icon
-         */
-        fun shouldConstrainToTopOfLockIcon(): Boolean
-
-        /** Destroys this binding, releases resources, and cancels any coroutines. */
-        fun destroy()
-    }
-
-    /** Binds the view to the view-model, continuing to update the former based on the latter. */
-    @Deprecated("Deprecated as part of b/278057014")
-    @SuppressLint("ClickableViewAccessibility")
-    @JvmStatic
-    fun bind(
-        view: ViewGroup,
-        viewModel: KeyguardBottomAreaViewModel,
-        falsingManager: FalsingManager?,
-        vibratorHelper: VibratorHelper?,
-        activityStarter: ActivityStarter?,
-        messageDisplayer: (Int) -> Unit,
-    ): Binding {
-        val ambientIndicationArea: View? = view.findViewById(R.id.ambient_indication_container)
-        val startButton: ImageView = view.requireViewById(R.id.start_button)
-        val endButton: ImageView = view.requireViewById(R.id.end_button)
-        val overlayContainer: View = view.requireViewById(R.id.overlay_container)
-        val settingsMenu: LaunchableLinearLayout =
-            view.requireViewById(R.id.keyguard_settings_button)
-
-        startButton.setOnApplyWindowInsetsListener { inView, windowInsets ->
-            val bottomInset = windowInsets.displayCutout?.safeInsetBottom ?: 0
-            val marginBottom =
-                inView.resources.getDimension(R.dimen.keyguard_affordance_vertical_offset).toInt()
-            inView.layoutParams =
-                (inView.layoutParams as MarginLayoutParams).apply {
-                    setMargins(
-                        inView.marginLeft,
-                        inView.marginTop,
-                        inView.marginRight,
-                        marginBottom + bottomInset
-                    )
-                }
-            WindowInsets.CONSUMED
-        }
-
-        endButton.setOnApplyWindowInsetsListener { inView, windowInsets ->
-            val bottomInset = windowInsets.displayCutout?.safeInsetBottom ?: 0
-            val marginBottom =
-                inView.resources.getDimension(R.dimen.keyguard_affordance_vertical_offset).toInt()
-            inView.layoutParams =
-                (inView.layoutParams as MarginLayoutParams).apply {
-                    setMargins(
-                        inView.marginLeft,
-                        inView.marginTop,
-                        inView.marginRight,
-                        marginBottom + bottomInset
-                    )
-                }
-            WindowInsets.CONSUMED
-        }
-
-        view.clipChildren = false
-        view.clipToPadding = false
-        view.setOnTouchListener { _, event ->
-            if (settingsMenu.isVisible) {
-                val hitRect = Rect()
-                settingsMenu.getHitRect(hitRect)
-                if (!hitRect.contains(event.x.toInt(), event.y.toInt())) {
-                    viewModel.onTouchedOutsideLockScreenSettingsMenu()
-                }
-            }
-
-            false
-        }
-
-        val configurationBasedDimensions = MutableStateFlow(loadFromResources(view))
-
-        val disposableHandle =
-            view.repeatWhenAttached {
-                repeatOnLifecycle(Lifecycle.State.STARTED) {
-                    // If updated, be sure to update [KeyguardQuickAffordanceViewBinder.kt]
-                    launch("$TAG#viewModel.startButton") {
-                        viewModel.startButton.collect { buttonModel ->
-                            updateButton(
-                                view = startButton,
-                                viewModel = buttonModel,
-                                falsingManager = falsingManager,
-                                messageDisplayer = messageDisplayer,
-                                vibratorHelper = vibratorHelper,
-                            )
-                        }
-                    }
-
-                    // If updated, be sure to update [KeyguardQuickAffordanceViewBinder.kt]
-                    launch("$TAG#viewModel.endButton") {
-                        viewModel.endButton.collect { buttonModel ->
-                            updateButton(
-                                view = endButton,
-                                viewModel = buttonModel,
-                                falsingManager = falsingManager,
-                                messageDisplayer = messageDisplayer,
-                                vibratorHelper = vibratorHelper,
-                            )
-                        }
-                    }
-
-                    launch("$TAG#viewModel.isOverlayContainerVisible") {
-                        viewModel.isOverlayContainerVisible.collect { isVisible ->
-                            overlayContainer.visibility =
-                                if (isVisible) {
-                                    View.VISIBLE
-                                } else {
-                                    View.INVISIBLE
-                                }
-                        }
-                    }
-
-                    launch("$TAG#viewModel.alpha") {
-                        viewModel.alpha.collect { alpha ->
-                            ambientIndicationArea?.apply {
-                                this.importantForAccessibility =
-                                    if (alpha == 0f) {
-                                        View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
-                                    } else {
-                                        View.IMPORTANT_FOR_ACCESSIBILITY_AUTO
-                                    }
-                                this.alpha = alpha
-                            }
-                        }
-                    }
-
-                    // If updated, be sure to update [KeyguardQuickAffordanceViewBinder.kt]
-                    launch("$TAG#updateButtonAlpha") {
-                        updateButtonAlpha(
-                            view = startButton,
-                            viewModel = viewModel.startButton,
-                            alphaFlow = viewModel.alpha,
-                        )
-                    }
-
-                    // If updated, be sure to update [KeyguardQuickAffordanceViewBinder.kt]
-                    launch("$TAG#updateButtonAlpha") {
-                        updateButtonAlpha(
-                            view = endButton,
-                            viewModel = viewModel.endButton,
-                            alphaFlow = viewModel.alpha,
-                        )
-                    }
-
-                    launch("$TAG#viewModel.indicationAreaTranslationX") {
-                        viewModel.indicationAreaTranslationX.collect { translationX ->
-                            ambientIndicationArea?.translationX = translationX
-                        }
-                    }
-
-                    launch("$TAG#viewModel.indicationAreaTranslationY") {
-                        configurationBasedDimensions
-                            .map { it.defaultBurnInPreventionYOffsetPx }
-                            .flatMapLatest { defaultBurnInOffsetY ->
-                                viewModel.indicationAreaTranslationY(defaultBurnInOffsetY)
-                            }
-                            .collect { translationY ->
-                                ambientIndicationArea?.translationY = translationY
-                            }
-                    }
-
-                    // If updated, be sure to update [KeyguardQuickAffordanceViewBinder.kt]
-                    launch("$TAG#startButton.updateLayoutParams<ViewGroup") {
-                        configurationBasedDimensions.collect { dimensions ->
-                            startButton.updateLayoutParams<ViewGroup.LayoutParams> {
-                                width = dimensions.buttonSizePx.width
-                                height = dimensions.buttonSizePx.height
-                            }
-                            endButton.updateLayoutParams<ViewGroup.LayoutParams> {
-                                width = dimensions.buttonSizePx.width
-                                height = dimensions.buttonSizePx.height
-                            }
-                        }
-                    }
-
-                    launch("$TAG#viewModel.settingsMenuViewModel") {
-                        viewModel.settingsMenuViewModel.isVisible.distinctUntilChanged().collect {
-                            isVisible ->
-                            settingsMenu.animateVisibility(visible = isVisible)
-                            if (isVisible) {
-                                vibratorHelper?.vibrate(KeyguardBottomAreaVibrations.Activated)
-                                settingsMenu.setOnTouchListener(
-                                    KeyguardSettingsButtonOnTouchListener(
-                                        viewModel = viewModel.settingsMenuViewModel,
-                                    )
-                                )
-                                IconViewBinder.bind(
-                                    icon = viewModel.settingsMenuViewModel.icon,
-                                    view = settingsMenu.requireViewById(R.id.icon),
-                                )
-                                TextViewBinder.bind(
-                                    view = settingsMenu.requireViewById(R.id.text),
-                                    viewModel = viewModel.settingsMenuViewModel.text,
-                                )
-                            }
-                        }
-                    }
-
-                    // activityStarter will only be null when rendering the preview that
-                    // shows up in the Wallpaper Picker app. If we do that, then the
-                    // settings menu should never be visible.
-                    if (activityStarter != null) {
-                        launch("$TAG#viewModel.settingsMenuViewModel") {
-                            viewModel.settingsMenuViewModel.shouldOpenSettings
-                                .filter { it }
-                                .collect {
-                                    navigateToLockScreenSettings(
-                                        activityStarter = activityStarter,
-                                        view = settingsMenu,
-                                    )
-                                    viewModel.settingsMenuViewModel.onSettingsShown()
-                                }
-                        }
-                    }
-                }
-            }
-
-        return object : Binding {
-            override fun onConfigurationChanged() {
-                configurationBasedDimensions.value = loadFromResources(view)
-            }
-
-            override fun shouldConstrainToTopOfLockIcon(): Boolean =
-                viewModel.shouldConstrainToTopOfLockIcon()
-
-            override fun destroy() {
-                disposableHandle.dispose()
-            }
-        }
-    }
-
-    @Deprecated("Deprecated as part of b/278057014")
-    // If updated, be sure to update [KeyguardQuickAffordanceViewBinder.kt]
-    @SuppressLint("ClickableViewAccessibility")
-    private fun updateButton(
-        view: ImageView,
-        viewModel: KeyguardQuickAffordanceViewModel,
-        falsingManager: FalsingManager?,
-        messageDisplayer: (Int) -> Unit,
-        vibratorHelper: VibratorHelper?,
-    ) {
-        if (!viewModel.isVisible) {
-            view.isInvisible = true
-            return
-        }
-
-        if (!view.isVisible) {
-            view.isVisible = true
-            if (viewModel.animateReveal) {
-                view.alpha = 0f
-                view.translationY = view.height / 2f
-                view
-                    .animate()
-                    .alpha(1f)
-                    .translationY(0f)
-                    .setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN)
-                    .setDuration(EXIT_DOZE_BUTTON_REVEAL_ANIMATION_DURATION_MS)
-                    .start()
-            }
-        }
-
-        IconViewBinder.bind(viewModel.icon, view)
-
-        (view.drawable as? Animatable2)?.let { animatable ->
-            (viewModel.icon as? Icon.Resource)?.res?.let { iconResourceId ->
-                // Always start the animation (we do call stop() below, if we need to skip it).
-                animatable.start()
-
-                if (view.tag != iconResourceId) {
-                    // Here when we haven't run the animation on a previous update.
-                    //
-                    // Save the resource ID for next time, so we know not to re-animate the same
-                    // animation again.
-                    view.tag = iconResourceId
-                } else {
-                    // Here when we've already done this animation on a previous update and want to
-                    // skip directly to the final frame of the animation to avoid running it.
-                    //
-                    // By calling stop after start, we go to the final frame of the animation.
-                    animatable.stop()
-                }
-            }
-        }
-
-        view.isActivated = viewModel.isActivated
-        view.drawable.setTint(
-            view.context.getColor(
-                if (viewModel.isActivated) {
-                    com.android.internal.R.color.materialColorOnPrimaryFixed
-                } else {
-                    com.android.internal.R.color.materialColorOnSurface
-                }
-            )
-        )
-
-        view.backgroundTintList =
-            if (!viewModel.isSelected) {
-                ColorStateList.valueOf(
-                    view.context.getColor(
-                        if (viewModel.isActivated) {
-                            com.android.internal.R.color.materialColorPrimaryFixed
-                        } else {
-                            com.android.internal.R.color.materialColorSurfaceContainerHigh
-                        }
-                    )
-                )
-            } else {
-                null
-            }
-        view
-            .animate()
-            .scaleX(if (viewModel.isSelected) SCALE_SELECTED_BUTTON else 1f)
-            .scaleY(if (viewModel.isSelected) SCALE_SELECTED_BUTTON else 1f)
-            .start()
-
-        view.isClickable = viewModel.isClickable
-        if (viewModel.isClickable) {
-            if (viewModel.useLongPress) {
-                val onTouchListener =
-                    KeyguardQuickAffordanceOnTouchListener(
-                        view,
-                        viewModel,
-                        messageDisplayer,
-                        vibratorHelper,
-                        falsingManager,
-                    )
-                view.setOnTouchListener(onTouchListener)
-                view.setOnClickListener {
-                    messageDisplayer.invoke(R.string.keyguard_affordance_press_too_short)
-                    val amplitude =
-                        view.context.resources
-                            .getDimensionPixelSize(R.dimen.keyguard_affordance_shake_amplitude)
-                            .toFloat()
-                    val shakeAnimator =
-                        ObjectAnimator.ofFloat(
-                            view,
-                            "translationX",
-                            -amplitude / 2,
-                            amplitude / 2,
-                        )
-                    shakeAnimator.duration =
-                        KeyguardBottomAreaVibrations.ShakeAnimationDuration.inWholeMilliseconds
-                    shakeAnimator.interpolator =
-                        CycleInterpolator(KeyguardBottomAreaVibrations.ShakeAnimationCycles)
-                    shakeAnimator.doOnEnd { view.translationX = 0f }
-                    shakeAnimator.start()
-
-                    vibratorHelper?.vibrate(KeyguardBottomAreaVibrations.Shake)
-                }
-                view.onLongClickListener =
-                    OnLongClickListener(falsingManager, viewModel, vibratorHelper, onTouchListener)
-            } else {
-                view.setOnClickListener(OnClickListener(viewModel, checkNotNull(falsingManager)))
-            }
-        } else {
-            view.onLongClickListener = null
-            view.setOnClickListener(null)
-            view.setOnTouchListener(null)
-        }
-
-        view.isSelected = viewModel.isSelected
-    }
-
-    @Deprecated("Deprecated as part of b/278057014")
-    // If updated, be sure to update [KeyguardQuickAffordanceViewBinder.kt]
-    private suspend fun updateButtonAlpha(
-        view: View,
-        viewModel: Flow<KeyguardQuickAffordanceViewModel>,
-        alphaFlow: Flow<Float>,
-    ) {
-        combine(viewModel.map { it.isDimmed }, alphaFlow) { isDimmed, alpha ->
-                if (isDimmed) DIM_ALPHA else alpha
-            }
-            .collect { view.alpha = it }
-    }
-
-    @Deprecated("Deprecated as part of b/278057014")
-    private fun View.animateVisibility(visible: Boolean) {
-        animate()
-            .withStartAction {
-                if (visible) {
-                    alpha = 0f
-                    isVisible = true
-                }
-            }
-            .alpha(if (visible) 1f else 0f)
-            .withEndAction {
-                if (!visible) {
-                    isVisible = false
-                }
-            }
-            .start()
-    }
-
-    @Deprecated("Deprecated as part of b/278057014")
-    // If updated, be sure to update [KeyguardQuickAffordanceViewBinder.kt]
-    private class OnLongClickListener(
-        private val falsingManager: FalsingManager?,
-        private val viewModel: KeyguardQuickAffordanceViewModel,
-        private val vibratorHelper: VibratorHelper?,
-        private val onTouchListener: KeyguardQuickAffordanceOnTouchListener
-    ) : View.OnLongClickListener {
-        override fun onLongClick(view: View): Boolean {
-            if (falsingManager?.isFalseLongTap(FalsingManager.MODERATE_PENALTY) == true) {
-                return true
-            }
-
-            if (viewModel.configKey != null) {
-                viewModel.onClicked(
-                    KeyguardQuickAffordanceViewModel.OnClickedParameters(
-                        configKey = viewModel.configKey,
-                        expandable = Expandable.fromView(view),
-                        slotId = viewModel.slotId,
-                    )
-                )
-                vibratorHelper?.vibrate(
-                    if (viewModel.isActivated) {
-                        KeyguardBottomAreaVibrations.Activated
-                    } else {
-                        KeyguardBottomAreaVibrations.Deactivated
-                    }
-                )
-            }
-
-            onTouchListener.cancel()
-            return true
-        }
-
-        override fun onLongClickUseDefaultHapticFeedback(view: View) = false
-    }
-
-    @Deprecated("Deprecated as part of b/278057014")
-    // If updated, be sure to update [KeyguardQuickAffordanceViewBinder.kt]
-    private class OnClickListener(
-        private val viewModel: KeyguardQuickAffordanceViewModel,
-        private val falsingManager: FalsingManager,
-    ) : View.OnClickListener {
-        override fun onClick(view: View) {
-            if (falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
-                return
-            }
-
-            if (viewModel.configKey != null) {
-                viewModel.onClicked(
-                    KeyguardQuickAffordanceViewModel.OnClickedParameters(
-                        configKey = viewModel.configKey,
-                        expandable = Expandable.fromView(view),
-                        slotId = viewModel.slotId,
-                    )
-                )
-            }
-        }
-    }
-
-    @Deprecated("Deprecated as part of b/278057014")
-    private fun loadFromResources(view: View): ConfigurationBasedDimensions {
-        return ConfigurationBasedDimensions(
-            defaultBurnInPreventionYOffsetPx =
-                view.resources.getDimensionPixelOffset(R.dimen.default_burn_in_prevention_offset),
-            buttonSizePx =
-                Size(
-                    view.resources.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_width),
-                    view.resources.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_height),
-                ),
-        )
-    }
-
-    @Deprecated("Deprecated as part of b/278057014")
-    /** Opens the wallpaper picker screen after the device is unlocked by the user. */
-    private fun navigateToLockScreenSettings(
-        activityStarter: ActivityStarter,
-        view: View,
-    ) {
-        activityStarter.postStartActivityDismissingKeyguard(
-            WallpaperPickerIntentUtils.getIntent(view.context, LAUNCH_SOURCE_KEYGUARD),
-            /* delay= */ 0,
-            /* animationController= */ ActivityTransitionAnimator.Controller.fromView(view),
-            /* customMessage= */ view.context.getString(R.string.keyguard_unlock_to_customize_ls)
-        )
-    }
-
-    @Deprecated("Deprecated as part of b/278057014")
-    // If updated, be sure to update [KeyguardQuickAffordanceViewBinder.kt]
-    private data class ConfigurationBasedDimensions(
-        val defaultBurnInPreventionYOffsetPx: Int,
-        val buttonSizePx: Size,
-    )
-}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt
index 273d763..0a958e9 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt
@@ -27,7 +27,6 @@
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.repeatOnLifecycle
 import com.android.app.tracing.coroutines.launchTraced as launch
-import com.android.systemui.keyguard.MigrateClocksToBlueprint
 import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
 import com.android.systemui.keyguard.shared.model.ClockSize
@@ -73,7 +72,6 @@
                     // When changing to new clock, we need to remove old views from burnInLayer
                     var lastClock: ClockController? = null
                     launch {
-                            if (!MigrateClocksToBlueprint.isEnabled) return@launch
                             viewModel.currentClock.collect { currentClock ->
                                 if (lastClock != currentClock) {
                                     cleanupClockViews(
@@ -99,7 +97,6 @@
                         }
 
                     launch {
-                        if (!MigrateClocksToBlueprint.isEnabled) return@launch
                         viewModel.clockSize.collect { clockSize ->
                             updateBurnInLayer(keyguardRootView, viewModel, clockSize)
                             blueprintInteractor.refreshBlueprint(Type.ClockSize)
@@ -107,7 +104,6 @@
                     }
 
                     launch {
-                        if (!MigrateClocksToBlueprint.isEnabled) return@launch
                         viewModel.clockShouldBeCentered.collect {
                             viewModel.currentClock.value?.let {
                                 // TODO(b/301502635): remove "!it.config.useCustomClockScene" when
@@ -125,7 +121,6 @@
                     }
 
                     launch {
-                        if (!MigrateClocksToBlueprint.isEnabled) return@launch
                         combine(
                                 viewModel.hasAodIcons,
                                 rootViewModel.isNotifIconContainerVisible.map { it.value },
@@ -143,7 +138,6 @@
                     }
 
                     launch {
-                        if (!MigrateClocksToBlueprint.isEnabled) return@launch
                         aodBurnInViewModel.movement.collect { burnInModel ->
                             viewModel.currentClock.value
                                 ?.largeClock
@@ -159,7 +153,6 @@
                     }
 
                     launch {
-                        if (!MigrateClocksToBlueprint.isEnabled) return@launch
                         viewModel.largeClockTextSize.collect { fontSizePx ->
                             viewModel.currentClock.value
                                 ?.largeClock
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt
index 8b947a3..92b49ed 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt
@@ -23,8 +23,6 @@
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.repeatOnLifecycle
 import com.android.app.tracing.coroutines.launchTraced as launch
-import com.android.systemui.keyguard.KeyguardBottomAreaRefactor
-import com.android.systemui.keyguard.MigrateClocksToBlueprint
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardIndicationAreaViewModel
 import com.android.systemui.lifecycle.repeatWhenAttached
 import com.android.systemui.res.R
@@ -75,16 +73,6 @@
         disposables +=
             view.repeatWhenAttached {
                 repeatOnLifecycle(Lifecycle.State.STARTED) {
-                    launch("$TAG#viewModel.alpha") {
-                        // Do not independently apply alpha, as [KeyguardRootViewModel] should work
-                        // for this and all its children
-                        if (
-                            !(MigrateClocksToBlueprint.isEnabled ||
-                                KeyguardBottomAreaRefactor.isEnabled)
-                        ) {
-                            viewModel.alpha.collect { alpha -> view.alpha = alpha }
-                        }
-                    }
 
                     launch("$TAG#viewModel.indicationAreaTranslationX") {
                         viewModel.indicationAreaTranslationX.collect { translationX ->
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt
index c0b3d83..1964cb2 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt
@@ -136,8 +136,16 @@
         viewModel: KeyguardPreviewClockViewModel,
     ) {
         val cs = ConstraintSet().apply { clone(rootView) }
-        previewClock.largeClock.layout.applyPreviewConstraints(clockPreviewConfig, cs)
-        previewClock.smallClock.layout.applyPreviewConstraints(clockPreviewConfig, cs)
+
+        val configWithUpdatedLockId =
+            if (rootView.getViewById(lockId) != null) {
+                clockPreviewConfig.copy(lockId = lockId)
+            } else {
+                clockPreviewConfig
+            }
+
+        previewClock.largeClock.layout.applyPreviewConstraints(configWithUpdatedLockId, cs)
+        previewClock.smallClock.layout.applyPreviewConstraints(configWithUpdatedLockId, cs)
 
         // When selectedClockSize is the initial value, make both clocks invisible to avoid
         // flickering
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt
index 5c8a234..8725cdd 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt
@@ -71,9 +71,6 @@
 
     /**
      * Defines interface for an object that acts as the binding between the view and its view-model.
-     *
-     * Users of the [KeyguardBottomAreaViewBinder] class should use this to control the binder after
-     * it is bound.
      */
     interface Binding {
         /** Notifies that device configuration has changed. */
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
index f121aab..a2ce4ec 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
@@ -31,14 +31,12 @@
 import android.view.View.VISIBLE
 import android.view.ViewGroup
 import android.view.ViewGroup.OnHierarchyChangeListener
-import android.view.ViewPropertyAnimator
 import android.view.WindowInsets
 import androidx.activity.OnBackPressedDispatcher
 import androidx.activity.OnBackPressedDispatcherOwner
 import androidx.activity.setViewTreeOnBackPressedDispatcherOwner
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.repeatOnLifecycle
-import com.android.app.animation.Interpolators
 import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.internal.jank.InteractionJankMonitor
 import com.android.internal.jank.InteractionJankMonitor.CUJ_SCREEN_OFF_SHOW_AOD
@@ -54,9 +52,7 @@
 import com.android.systemui.common.ui.view.onTouchListener
 import com.android.systemui.customization.R as customR
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryHapticsInteractor
-import com.android.systemui.keyguard.KeyguardBottomAreaRefactor
 import com.android.systemui.keyguard.KeyguardViewMediator
-import com.android.systemui.keyguard.MigrateClocksToBlueprint
 import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.TransitionState
@@ -90,12 +86,9 @@
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.DisposableHandle
 import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.coroutineScope
-import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.stateIn
 import kotlinx.coroutines.flow.update
-import com.android.app.tracing.coroutines.launchTraced as launch
 
 /** Bind occludingAppDeviceEntryMessageViewModel to run whenever the keyguard view is attached. */
 @OptIn(ExperimentalCoroutinesApi::class)
@@ -125,35 +118,33 @@
         val disposables = DisposableHandles()
         val childViews = mutableMapOf<Int, View>()
 
-        if (KeyguardBottomAreaRefactor.isEnabled) {
-            disposables +=
-                view.onTouchListener { _, event ->
-                    var consumed = false
-                    if (falsingManager?.isFalseTap(FalsingManager.LOW_PENALTY) == false) {
-                        // signifies a primary button click down has reached keyguardrootview
-                        // we need to return true here otherwise an ACTION_UP will never arrive
-                        if (Flags.nonTouchscreenDevicesBypassFalsing()) {
-                            if (
-                                event.action == MotionEvent.ACTION_DOWN &&
-                                    event.buttonState == MotionEvent.BUTTON_PRIMARY &&
-                                    !event.isTouchscreenSource()
-                            ) {
-                                consumed = true
-                            } else if (
-                                event.action == MotionEvent.ACTION_UP &&
-                                    !event.isTouchscreenSource()
-                            ) {
-                                statusBarKeyguardViewManager?.showBouncer(true)
-                                consumed = true
-                            }
+        disposables +=
+            view.onTouchListener { _, event ->
+                var consumed = false
+                if (falsingManager?.isFalseTap(FalsingManager.LOW_PENALTY) == false) {
+                    // signifies a primary button click down has reached keyguardrootview
+                    // we need to return true here otherwise an ACTION_UP will never arrive
+                    if (Flags.nonTouchscreenDevicesBypassFalsing()) {
+                        if (
+                            event.action == MotionEvent.ACTION_DOWN &&
+                            event.buttonState == MotionEvent.BUTTON_PRIMARY &&
+                            !event.isTouchscreenSource()
+                        ) {
+                            consumed = true
+                        } else if (
+                            event.action == MotionEvent.ACTION_UP &&
+                            !event.isTouchscreenSource()
+                        ) {
+                            statusBarKeyguardViewManager?.showBouncer(true)
+                            consumed = true
                         }
-                        viewModel.setRootViewLastTapPosition(
-                            Point(event.x.toInt(), event.y.toInt())
-                        )
                     }
-                    consumed
+                    viewModel.setRootViewLastTapPosition(
+                        Point(event.x.toInt(), event.y.toInt())
+                    )
                 }
-        }
+                consumed
+            }
 
         val burnInParams = MutableStateFlow(BurnInParameters())
         val viewState = ViewStateAccessor(alpha = { view.alpha })
@@ -161,21 +152,19 @@
         disposables +=
             view.repeatWhenAttached(mainImmediateDispatcher) {
                 repeatOnLifecycle(Lifecycle.State.CREATED) {
-                    if (MigrateClocksToBlueprint.isEnabled) {
-                        launch("$TAG#topClippingBounds") {
-                            val clipBounds = Rect()
-                            viewModel.topClippingBounds.collect { clipTop ->
-                                if (clipTop == null) {
-                                    view.setClipBounds(null)
-                                } else {
-                                    clipBounds.apply {
-                                        top = clipTop
-                                        left = view.getLeft()
-                                        right = view.getRight()
-                                        bottom = view.getBottom()
-                                    }
-                                    view.setClipBounds(clipBounds)
+                    launch("$TAG#topClippingBounds") {
+                        val clipBounds = Rect()
+                        viewModel.topClippingBounds.collect { clipTop ->
+                            if (clipTop == null) {
+                                view.setClipBounds(null)
+                            } else {
+                                clipBounds.apply {
+                                    top = clipTop
+                                    left = view.getLeft()
+                                    right = view.getRight()
+                                    bottom = view.getBottom()
                                 }
+                                view.setClipBounds(clipBounds)
                             }
                         }
                     }
@@ -183,47 +172,41 @@
                     launch("$TAG#alpha") {
                         viewModel.alpha(viewState).collect { alpha ->
                             view.alpha = alpha
-                            if (KeyguardBottomAreaRefactor.isEnabled) {
-                                childViews[statusViewId]?.alpha = alpha
-                                childViews[burnInLayerId]?.alpha = alpha
-                            }
+                            childViews[statusViewId]?.alpha = alpha
+                            childViews[burnInLayerId]?.alpha = alpha
                         }
                     }
 
-                    if (MigrateClocksToBlueprint.isEnabled) {
-                        launch("$TAG#translationY") {
-                            // When translation happens in burnInLayer, it won't be weather clock
-                            // large clock isn't added to burnInLayer due to its scale transition
-                            // so we also need to add translation to it here
-                            // same as translationX
-                            viewModel.translationY.collect { y ->
-                                childViews[burnInLayerId]?.translationY = y
-                                childViews[largeClockId]?.translationY = y
-                                childViews[aodNotificationIconContainerId]?.translationY = y
-                            }
+                    launch("$TAG#translationY") {
+                        // When translation happens in burnInLayer, it won't be weather clock large
+                        // clock isn't added to burnInLayer due to its scale transition so we also
+                        // need to add translation to it here same as translationX
+                        viewModel.translationY.collect { y ->
+                            childViews[burnInLayerId]?.translationY = y
+                            childViews[largeClockId]?.translationY = y
+                            childViews[aodNotificationIconContainerId]?.translationY = y
                         }
+                    }
 
-                        launch("$TAG#translationX") {
-                            viewModel.translationX.collect { state ->
-                                val px = state.value ?: return@collect
-                                when {
-                                    state.isToOrFrom(KeyguardState.AOD) -> {
-                                        // Large Clock is not translated in the x direction
-                                        childViews[burnInLayerId]?.translationX = px
-                                        childViews[aodNotificationIconContainerId]?.translationX =
-                                            px
-                                    }
-                                    state.isToOrFrom(KeyguardState.GLANCEABLE_HUB) -> {
-                                        for ((key, childView) in childViews.entries) {
-                                            when (key) {
-                                                indicationArea,
-                                                startButton,
-                                                endButton,
-                                                deviceEntryIcon -> {
-                                                    // Do not move these views
-                                                }
-                                                else -> childView.translationX = px
+                    launch("$TAG#translationX") {
+                        viewModel.translationX.collect { state ->
+                            val px = state.value ?: return@collect
+                            when {
+                                state.isToOrFrom(KeyguardState.AOD) -> {
+                                    // Large Clock is not translated in the x direction
+                                    childViews[burnInLayerId]?.translationX = px
+                                    childViews[aodNotificationIconContainerId]?.translationX = px
+                                }
+                                state.isToOrFrom(KeyguardState.GLANCEABLE_HUB) -> {
+                                    for ((key, childView) in childViews.entries) {
+                                        when (key) {
+                                            indicationArea,
+                                            startButton,
+                                            endButton,
+                                            deviceEntryIcon -> {
+                                                // Do not move these views
                                             }
+                                            else -> childView.translationX = px
                                         }
                                     }
                                 }
@@ -263,95 +246,92 @@
                         }
                     }
 
-                    if (MigrateClocksToBlueprint.isEnabled) {
-                        launch {
-                            viewModel.burnInLayerVisibility.collect { visibility ->
-                                childViews[burnInLayerId]?.visibility = visibility
-                            }
+                    launch {
+                        viewModel.burnInLayerVisibility.collect { visibility ->
+                            childViews[burnInLayerId]?.visibility = visibility
                         }
+                    }
 
-                        launch {
-                            viewModel.burnInLayerAlpha.collect { alpha ->
-                                childViews[statusViewId]?.alpha = alpha
-                            }
+                    launch {
+                        viewModel.burnInLayerAlpha.collect { alpha ->
+                            childViews[statusViewId]?.alpha = alpha
                         }
+                    }
 
-                        launch {
-                            viewModel.lockscreenStateAlpha(viewState).collect { alpha ->
-                                childViews[statusViewId]?.alpha = alpha
-                            }
+                    launch {
+                        viewModel.lockscreenStateAlpha(viewState).collect { alpha ->
+                            childViews[statusViewId]?.alpha = alpha
                         }
+                    }
 
-                        launch {
-                            viewModel.scale.collect { scaleViewModel ->
-                                if (scaleViewModel.scaleClockOnly) {
-                                    // For clocks except weather clock, we have scale transition
-                                    // besides translate
-                                    childViews[largeClockId]?.let {
-                                        it.scaleX = scaleViewModel.scale
-                                        it.scaleY = scaleViewModel.scale
-                                    }
+                    launch {
+                        viewModel.scale.collect { scaleViewModel ->
+                            if (scaleViewModel.scaleClockOnly) {
+                                // For clocks except weather clock, we have scale transition besides
+                                // translate
+                                childViews[largeClockId]?.let {
+                                    it.scaleX = scaleViewModel.scale
+                                    it.scaleY = scaleViewModel.scale
                                 }
                             }
                         }
+                    }
 
-                        launch {
-                            blueprintViewModel.currentTransition.collect { currentTransition ->
-                                // When blueprint/clock transitions end (null), make sure NSSL is in
-                                // the right place
-                                if (currentTransition == null) {
-                                    childViews[nsslPlaceholderId]?.let { notificationListPlaceholder
-                                        ->
-                                        viewModel.onNotificationContainerBoundsChanged(
-                                            notificationListPlaceholder.top.toFloat(),
-                                            notificationListPlaceholder.bottom.toFloat(),
-                                            animate = true,
-                                        )
-                                    }
-                                }
-                            }
-                        }
-
-                        launch {
-                            val iconsAppearTranslationPx =
-                                configuration
-                                    .getDimensionPixelSize(R.dimen.shelf_appear_translation)
-                                    .stateIn(this)
-                            viewModel.isNotifIconContainerVisible.collect { isVisible ->
-                                if (isVisible.value) {
-                                    blueprintViewModel.refreshBlueprint()
-                                }
-                                childViews[aodNotificationIconContainerId]
-                                    ?.setAodNotifIconContainerIsVisible(
-                                        isVisible,
-                                        iconsAppearTranslationPx.value,
-                                        screenOffAnimationController,
+                    launch {
+                        blueprintViewModel.currentTransition.collect { currentTransition ->
+                            // When blueprint/clock transitions end (null), make sure NSSL is in the
+                            // right place
+                            if (currentTransition == null) {
+                                childViews[nsslPlaceholderId]?.let { notificationListPlaceholder ->
+                                    viewModel.onNotificationContainerBoundsChanged(
+                                        notificationListPlaceholder.top.toFloat(),
+                                        notificationListPlaceholder.bottom.toFloat(),
+                                        animate = true,
                                     )
+                                }
                             }
                         }
+                    }
 
-                        interactionJankMonitor?.let { jankMonitor ->
-                            launch {
-                                viewModel.goneToAodTransition.collect {
-                                    when (it.transitionState) {
-                                        TransitionState.STARTED -> {
-                                            val clockId = clockInteractor.renderedClockId
-                                            val builder =
-                                                InteractionJankMonitor.Configuration.Builder
-                                                    .withView(CUJ_SCREEN_OFF_SHOW_AOD, view)
-                                                    .setTag(clockId)
-                                            jankMonitor.begin(builder)
-                                        }
-                                        TransitionState.CANCELED ->
-                                            jankMonitor.cancel(CUJ_SCREEN_OFF_SHOW_AOD)
-                                        TransitionState.FINISHED -> {
-                                            if (MigrateClocksToBlueprint.isEnabled) {
-                                                keyguardViewMediator?.maybeHandlePendingLock()
-                                            }
-                                            jankMonitor.end(CUJ_SCREEN_OFF_SHOW_AOD)
-                                        }
-                                        TransitionState.RUNNING -> Unit
+                    launch {
+                        val iconsAppearTranslationPx =
+                            configuration
+                                .getDimensionPixelSize(R.dimen.shelf_appear_translation)
+                                .stateIn(this)
+                        viewModel.isNotifIconContainerVisible.collect { isVisible ->
+                            if (isVisible.value) {
+                                blueprintViewModel.refreshBlueprint()
+                            }
+                            childViews[aodNotificationIconContainerId]
+                                ?.setAodNotifIconContainerIsVisible(
+                                    isVisible,
+                                    iconsAppearTranslationPx.value,
+                                    screenOffAnimationController,
+                                )
+                        }
+                    }
+
+                    interactionJankMonitor?.let { jankMonitor ->
+                        launch {
+                            viewModel.goneToAodTransition.collect {
+                                when (it.transitionState) {
+                                    TransitionState.STARTED -> {
+                                        val clockId = clockInteractor.renderedClockId
+                                        val builder =
+                                            InteractionJankMonitor.Configuration.Builder.withView(
+                                                    CUJ_SCREEN_OFF_SHOW_AOD,
+                                                    view,
+                                                )
+                                                .setTag(clockId)
+                                        jankMonitor.begin(builder)
                                     }
+                                    TransitionState.CANCELED ->
+                                        jankMonitor.cancel(CUJ_SCREEN_OFF_SHOW_AOD)
+                                    TransitionState.FINISHED -> {
+                                        keyguardViewMediator?.maybeHandlePendingLock()
+                                        jankMonitor.end(CUJ_SCREEN_OFF_SHOW_AOD)
+                                    }
+                                    TransitionState.RUNNING -> Unit
                                 }
                             }
                         }
@@ -406,13 +386,11 @@
                 }
             }
 
-        if (MigrateClocksToBlueprint.isEnabled) {
-            burnInParams.update { current ->
-                current.copy(
-                    translationX = { childViews[burnInLayerId]?.translationX },
-                    translationY = { childViews[burnInLayerId]?.translationY },
-                )
-            }
+        burnInParams.update { current ->
+            current.copy(
+                translationX = { childViews[burnInLayerId]?.translationX },
+                translationY = { childViews[burnInLayerId]?.translationY },
+            )
         }
 
         disposables +=
@@ -515,20 +493,16 @@
             burnInParams.update { current ->
                 current.copy(
                     minViewY =
-                        if (MigrateClocksToBlueprint.isEnabled) {
-                            // To ensure burn-in doesn't enroach the top inset, get the min top Y
-                            childViews.entries.fold(Int.MAX_VALUE) { currentMin, (viewId, view) ->
-                                min(
-                                    currentMin,
-                                    if (!isUserVisible(view)) {
-                                        Int.MAX_VALUE
-                                    } else {
-                                        view.getTop()
-                                    },
-                                )
-                            }
-                        } else {
-                            childViews[statusViewId]?.top ?: 0
+                        // To ensure burn-in doesn't enroach the top inset, get the min top Y
+                        childViews.entries.fold(Int.MAX_VALUE) { currentMin, (viewId, view) ->
+                            min(
+                                currentMin,
+                                if (!isUserVisible(view)) {
+                                    Int.MAX_VALUE
+                                } else {
+                                    view.getTop()
+                                },
+                            )
                         }
                 )
             }
@@ -542,28 +516,6 @@
         }
     }
 
-    suspend fun bindAodNotifIconVisibility(
-        view: View,
-        isVisible: Flow<AnimatedValue<Boolean>>,
-        configuration: ConfigurationState,
-        screenOffAnimationController: ScreenOffAnimationController,
-    ) {
-        if (MigrateClocksToBlueprint.isEnabled) {
-            throw IllegalStateException("should only be called in legacy code paths")
-        }
-        coroutineScope {
-            val iconAppearTranslationPx =
-                configuration.getDimensionPixelSize(R.dimen.shelf_appear_translation).stateIn(this)
-            isVisible.collect { isVisible ->
-                view.setAodNotifIconContainerIsVisible(
-                    isVisible = isVisible,
-                    iconsAppearTranslationPx = iconAppearTranslationPx.value,
-                    screenOffAnimationController = screenOffAnimationController,
-                )
-            }
-        }
-    }
-
     private fun View.setAodNotifIconContainerIsVisible(
         isVisible: AnimatedValue<Boolean>,
         iconsAppearTranslationPx: Int,
@@ -578,9 +530,6 @@
             }
         when {
             !isVisible.isAnimating -> {
-                if (!MigrateClocksToBlueprint.isEnabled) {
-                    translationY = 0f
-                }
                 visibility =
                     if (isVisible.value) {
                         alpha = 1f
@@ -591,7 +540,6 @@
                     }
             }
             else -> {
-                animateInIconTranslation()
                 if (isVisible.value) {
                     CrossFadeHelper.fadeIn(this, animatorListener)
                 } else {
@@ -601,19 +549,10 @@
         }
     }
 
-    private fun View.animateInIconTranslation() {
-        if (!MigrateClocksToBlueprint.isEnabled) {
-            animate().animateInIconTranslation().setDuration(AOD_ICONS_APPEAR_DURATION).start()
-        }
-    }
-
     private fun MotionEvent.isTouchscreenSource(): Boolean {
         return device?.supportsSource(InputDevice.SOURCE_TOUCHSCREEN) == true
     }
 
-    private fun ViewPropertyAnimator.animateInIconTranslation(): ViewPropertyAnimator =
-        setInterpolator(Interpolators.DECELERATE_QUINT).translationY(0f)
-
     private val statusViewId = R.id.keyguard_status_view
     private val burnInLayerId = R.id.burn_in_layer
     private val aodNotificationIconContainerId = R.id.aod_notification_icon_container
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSmartspaceViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSmartspaceViewBinder.kt
index de4a1b0..213083d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSmartspaceViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSmartspaceViewBinder.kt
@@ -22,7 +22,6 @@
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.repeatOnLifecycle
 import com.android.app.tracing.coroutines.launchTraced as launch
-import com.android.systemui.keyguard.MigrateClocksToBlueprint
 import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor
 import com.android.systemui.keyguard.ui.view.layout.blueprints.transitions.IntraBlueprintTransition.Config
 import com.android.systemui.keyguard.ui.view.layout.blueprints.transitions.IntraBlueprintTransition.Type
@@ -44,13 +43,12 @@
         return keyguardRootView.repeatWhenAttached {
             repeatOnLifecycle(Lifecycle.State.CREATED) {
                 launch("$TAG#clockViewModel.hasCustomWeatherDataDisplay") {
-                    if (!MigrateClocksToBlueprint.isEnabled) return@launch
                     clockViewModel.hasCustomWeatherDataDisplay.collect { hasCustomWeatherDataDisplay
                         ->
                         updateDateWeatherToBurnInLayer(
                             keyguardRootView,
                             clockViewModel,
-                            smartspaceViewModel
+                            smartspaceViewModel,
                         )
                         blueprintInteractor.refreshBlueprint(
                             Config(
@@ -63,7 +61,6 @@
                 }
 
                 launch("$TAG#smartspaceViewModel.bcSmartspaceVisibility") {
-                    if (!MigrateClocksToBlueprint.isEnabled) return@launch
                     smartspaceViewModel.bcSmartspaceVisibility.collect {
                         updateBCSmartspaceInBurnInLayer(keyguardRootView, clockViewModel)
                         blueprintInteractor.refreshBlueprint(
@@ -100,7 +97,7 @@
     private fun updateDateWeatherToBurnInLayer(
         keyguardRootView: ConstraintLayout,
         clockViewModel: KeyguardClockViewModel,
-        smartspaceViewModel: KeyguardSmartspaceViewModel
+        smartspaceViewModel: KeyguardSmartspaceViewModel,
     ) {
         if (clockViewModel.hasCustomWeatherDataDisplay.value) {
             removeDateWeatherFromBurnInLayer(keyguardRootView, smartspaceViewModel)
@@ -112,7 +109,7 @@
 
     private fun addDateWeatherToBurnInLayer(
         constraintLayout: ConstraintLayout,
-        smartspaceViewModel: KeyguardSmartspaceViewModel
+        smartspaceViewModel: KeyguardSmartspaceViewModel,
     ) {
         val burnInLayer = constraintLayout.requireViewById<Layer>(R.id.burn_in_layer)
         burnInLayer.apply {
@@ -129,7 +126,7 @@
 
     private fun removeDateWeatherFromBurnInLayer(
         constraintLayout: ConstraintLayout,
-        smartspaceViewModel: KeyguardSmartspaceViewModel
+        smartspaceViewModel: KeyguardSmartspaceViewModel,
     ) {
         val burnInLayer = constraintLayout.requireViewById<Layer>(R.id.burn_in_layer)
         burnInLayer.apply {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
index 85725d2..090b659 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
@@ -48,39 +48,28 @@
 import androidx.constraintlayout.widget.ConstraintSet.START
 import androidx.constraintlayout.widget.ConstraintSet.TOP
 import androidx.core.view.isInvisible
-import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.internal.policy.SystemBarUtils
 import com.android.keyguard.ClockEventController
 import com.android.keyguard.KeyguardClockSwitch
 import com.android.systemui.animation.view.LaunchableImageView
 import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor
 import com.android.systemui.broadcast.BroadcastDispatcher
-import com.android.systemui.common.ui.ConfigurationState
 import com.android.systemui.communal.ui.binder.CommunalTutorialIndicatorViewBinder
 import com.android.systemui.communal.ui.viewmodel.CommunalTutorialIndicatorViewModel
 import com.android.systemui.coroutines.newTracingContext
-import com.android.systemui.customization.R as customR
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.keyguard.KeyguardBottomAreaRefactor
 import com.android.systemui.keyguard.MigrateClocksToBlueprint
-import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
 import com.android.systemui.keyguard.shared.model.ClockSizeSetting
 import com.android.systemui.keyguard.ui.binder.KeyguardPreviewClockViewBinder
 import com.android.systemui.keyguard.ui.binder.KeyguardPreviewSmartspaceViewBinder
 import com.android.systemui.keyguard.ui.binder.KeyguardQuickAffordanceViewBinder
-import com.android.systemui.keyguard.ui.binder.KeyguardRootViewBinder
 import com.android.systemui.keyguard.ui.view.KeyguardRootView
 import com.android.systemui.keyguard.ui.view.layout.sections.DefaultShortcutsSection
-import com.android.systemui.keyguard.ui.viewmodel.KeyguardBlueprintViewModel
-import com.android.systemui.keyguard.ui.viewmodel.KeyguardBottomAreaViewModel
-import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardPreviewClockViewModel
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardPreviewSmartspaceViewModel
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordancesCombinedViewModel
-import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
-import com.android.systemui.keyguard.ui.viewmodel.OccludingAppDeviceEntryMessageViewModel
 import com.android.systemui.monet.ColorScheme
 import com.android.systemui.monet.Style
 import com.android.systemui.plugins.clocks.ClockController
@@ -97,9 +86,6 @@
 import com.android.systemui.shared.quickaffordance.shared.model.KeyguardPreviewConstants
 import com.android.systemui.statusbar.KeyguardIndicationController
 import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController
-import com.android.systemui.statusbar.phone.KeyguardBottomAreaView
-import com.android.systemui.statusbar.phone.ScreenOffAnimationController
-import com.android.systemui.temporarydisplay.chipbar.ChipbarCoordinator
 import com.android.systemui.util.kotlin.DisposableHandles
 import com.android.systemui.util.settings.SecureSettings
 import dagger.assisted.Assisted
@@ -115,6 +101,8 @@
 import kotlinx.coroutines.withContext
 import org.json.JSONException
 import org.json.JSONObject
+import com.android.app.tracing.coroutines.launchTraced as launch
+import com.android.systemui.customization.R as customR
 
 /** Renders the preview of the lock screen. */
 class KeyguardPreviewRenderer
@@ -128,29 +116,20 @@
     @Background private val backgroundDispatcher: CoroutineDispatcher,
     private val clockViewModel: KeyguardPreviewClockViewModel,
     private val smartspaceViewModel: KeyguardPreviewSmartspaceViewModel,
-    private val bottomAreaViewModel: KeyguardBottomAreaViewModel,
     private val quickAffordancesCombinedViewModel: KeyguardQuickAffordancesCombinedViewModel,
     displayManager: DisplayManager,
     private val windowManager: WindowManager,
-    private val configuration: ConfigurationState,
     private val clockController: ClockEventController,
     private val clockRegistry: ClockRegistry,
     private val broadcastDispatcher: BroadcastDispatcher,
     private val lockscreenSmartspaceController: LockscreenSmartspaceController,
     private val udfpsOverlayInteractor: UdfpsOverlayInteractor,
     private val indicationController: KeyguardIndicationController,
-    private val keyguardRootViewModel: KeyguardRootViewModel,
-    private val keyguardBlueprintViewModel: KeyguardBlueprintViewModel,
     @Assisted bundle: Bundle,
-    private val occludingAppDeviceEntryMessageViewModel: OccludingAppDeviceEntryMessageViewModel,
-    private val chipbarCoordinator: ChipbarCoordinator,
-    private val screenOffAnimationController: ScreenOffAnimationController,
     private val shadeInteractor: ShadeInteractor,
     private val secureSettings: SecureSettings,
     private val communalTutorialViewModel: CommunalTutorialIndicatorViewModel,
     private val defaultShortcutsSection: DefaultShortcutsSection,
-    private val keyguardClockInteractor: KeyguardClockInteractor,
-    private val keyguardClockViewModel: KeyguardClockViewModel,
     private val keyguardQuickAffordanceViewBinder: KeyguardQuickAffordanceViewBinder,
 ) {
     val hostToken: IBinder? = bundle.getBinder(KEY_HOST_TOKEN)
@@ -201,20 +180,12 @@
         disposables += DisposableHandle { coroutineScope.cancel() }
         clockController.setFallbackWeatherData(WeatherData.getPlaceholderWeatherData())
 
-        if (KeyguardBottomAreaRefactor.isEnabled) {
-            quickAffordancesCombinedViewModel.enablePreviewMode(
-                initiallySelectedSlotId =
-                    bundle.getString(KeyguardPreviewConstants.KEY_INITIALLY_SELECTED_SLOT_ID)
-                        ?: KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START,
-                shouldHighlightSelectedAffordance = shouldHighlightSelectedAffordance,
-            )
-        } else {
-            bottomAreaViewModel.enablePreviewMode(
-                initiallySelectedSlotId =
-                    bundle.getString(KeyguardPreviewConstants.KEY_INITIALLY_SELECTED_SLOT_ID),
-                shouldHighlightSelectedAffordance = shouldHighlightSelectedAffordance,
-            )
-        }
+        quickAffordancesCombinedViewModel.enablePreviewMode(
+            initiallySelectedSlotId =
+            bundle.getString(KeyguardPreviewConstants.KEY_INITIALLY_SELECTED_SLOT_ID)
+                ?: KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START,
+            shouldHighlightSelectedAffordance = shouldHighlightSelectedAffordance,
+        )
         if (MigrateClocksToBlueprint.isEnabled) {
             clockViewModel.shouldHighlightSelectedAffordance = shouldHighlightSelectedAffordance
         }
@@ -241,10 +212,6 @@
 
             setupKeyguardRootView(previewContext, rootView)
 
-            if (!KeyguardBottomAreaRefactor.isEnabled) {
-                setUpBottomArea(rootView)
-            }
-
             var displayInfo: DisplayInfo? = null
             display?.let {
                 displayInfo = DisplayInfo()
@@ -292,11 +259,7 @@
     }
 
     fun onSlotSelected(slotId: String) {
-        if (KeyguardBottomAreaRefactor.isEnabled) {
-            quickAffordancesCombinedViewModel.onPreviewSlotSelected(slotId = slotId)
-        } else {
-            bottomAreaViewModel.onPreviewSlotSelected(slotId = slotId)
-        }
+        quickAffordancesCombinedViewModel.onPreviewSlotSelected(slotId = slotId)
     }
 
     fun onPreviewQuickAffordanceSelected(slotId: String, quickAffordanceId: String) {
@@ -322,9 +285,7 @@
         isDestroyed = true
         lockscreenSmartspaceController.disconnect()
         disposables.dispose()
-        if (KeyguardBottomAreaRefactor.isEnabled) {
-            shortcutsBindings.forEach { it.destroy() }
-        }
+        shortcutsBindings.forEach { it.destroy() }
     }
 
     /**
@@ -387,47 +348,8 @@
         smartSpaceView?.alpha = if (shouldHighlightSelectedAffordance) DIM_ALPHA else 1.0f
     }
 
-    @Deprecated("Deprecated as part of b/278057014")
-    private fun setUpBottomArea(parentView: ViewGroup) {
-        val bottomAreaView =
-            LayoutInflater.from(context).inflate(R.layout.keyguard_bottom_area, parentView, false)
-                as KeyguardBottomAreaView
-        bottomAreaView.init(viewModel = bottomAreaViewModel)
-        parentView.addView(
-            bottomAreaView,
-            FrameLayout.LayoutParams(
-                FrameLayout.LayoutParams.MATCH_PARENT,
-                FrameLayout.LayoutParams.MATCH_PARENT,
-            ),
-        )
-    }
-
-    @OptIn(ExperimentalCoroutinesApi::class)
     private fun setupKeyguardRootView(previewContext: Context, rootView: FrameLayout) {
         val keyguardRootView = KeyguardRootView(previewContext, null)
-        if (!KeyguardBottomAreaRefactor.isEnabled) {
-            disposables +=
-                KeyguardRootViewBinder.bind(
-                    keyguardRootView,
-                    keyguardRootViewModel,
-                    keyguardBlueprintViewModel,
-                    configuration,
-                    occludingAppDeviceEntryMessageViewModel,
-                    chipbarCoordinator,
-                    screenOffAnimationController,
-                    shadeInteractor,
-                    keyguardClockInteractor,
-                    keyguardClockViewModel,
-                    null, // jank monitor not required for preview mode
-                    null, // device entry haptics not required preview mode
-                    null, // device entry haptics not required for preview mode
-                    null, // falsing manager not required for preview mode
-                    null, // keyguard view mediator is not required for preview mode
-                    null, // primary bouncer interactor is not required for preview mode
-                    mainDispatcher,
-                    null,
-                )
-        }
         rootView.addView(
             keyguardRootView,
             FrameLayout.LayoutParams(
@@ -441,9 +363,7 @@
             if (MigrateClocksToBlueprint.isEnabled) keyguardRootView else rootView,
         )
 
-        if (KeyguardBottomAreaRefactor.isEnabled) {
-            setupShortcuts(keyguardRootView)
-        }
+        setupShortcuts(keyguardRootView)
 
         if (!shouldHideClock) {
             setUpClock(previewContext, rootView)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/PrimaryBouncerTransition.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/PrimaryBouncerTransition.kt
new file mode 100644
index 0000000..cafc909
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/PrimaryBouncerTransition.kt
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.transitions
+
+import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerToPrimaryBouncerTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.AodToPrimaryBouncerTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.DozingToPrimaryBouncerTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.LockscreenToPrimaryBouncerTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToAodTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToDozingTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGlanceableHubTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToLockscreenTransitionViewModel
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.IntoSet
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+
+/**
+ * Each PrimaryBouncerTransition is responsible for updating various UI states based on the nature
+ * of the transition.
+ *
+ * MUST list implementing classes in dagger module [PrimaryBouncerTransitionModule].
+ */
+interface PrimaryBouncerTransition {
+    /** Radius of blur applied to the window's root view. */
+    val windowBlurRadius: Flow<Float>
+
+    companion object {
+        const val MAX_BACKGROUND_BLUR_RADIUS = 150f
+        const val MIN_BACKGROUND_BLUR_RADIUS = 0f
+    }
+}
+
+/**
+ * Module that installs all the transitions from different keyguard states to and away from the
+ * primary bouncer.
+ */
+@ExperimentalCoroutinesApi
+@Module
+interface PrimaryBouncerTransitionModule {
+    @Binds
+    @IntoSet
+    fun fromAod(impl: AodToPrimaryBouncerTransitionViewModel): PrimaryBouncerTransition
+
+    @Binds
+    @IntoSet
+    fun fromAlternateBouncer(
+        impl: AlternateBouncerToPrimaryBouncerTransitionViewModel
+    ): PrimaryBouncerTransition
+
+    @Binds
+    @IntoSet
+    fun fromDozing(impl: DozingToPrimaryBouncerTransitionViewModel): PrimaryBouncerTransition
+
+    @Binds
+    @IntoSet
+    fun fromLockscreen(
+        impl: LockscreenToPrimaryBouncerTransitionViewModel
+    ): PrimaryBouncerTransition
+
+    @Binds
+    @IntoSet
+    fun toAod(impl: PrimaryBouncerToAodTransitionViewModel): PrimaryBouncerTransition
+
+    @Binds
+    @IntoSet
+    fun toLockscreen(impl: PrimaryBouncerToLockscreenTransitionViewModel): PrimaryBouncerTransition
+
+    @Binds
+    @IntoSet
+    fun toDozing(impl: PrimaryBouncerToDozingTransitionViewModel): PrimaryBouncerTransition
+
+    @Binds
+    @IntoSet
+    fun toGlanceableHub(
+        impl: PrimaryBouncerToGlanceableHubTransitionViewModel
+    ): PrimaryBouncerTransition
+
+    @Binds
+    @IntoSet
+    fun toGone(impl: PrimaryBouncerToGoneTransitionViewModel): PrimaryBouncerTransition
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt
index 4c23adf..6f7872c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt
@@ -30,7 +30,6 @@
 import androidx.constraintlayout.widget.ConstraintSet.TOP
 import com.android.systemui.common.ui.ConfigurationState
 import com.android.systemui.customization.R as customR
-import com.android.systemui.keyguard.MigrateClocksToBlueprint
 import com.android.systemui.keyguard.shared.model.KeyguardSection
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
 import com.android.systemui.res.R
@@ -62,9 +61,6 @@
     private lateinit var nic: NotificationIconContainer
 
     override fun addViews(constraintLayout: ConstraintLayout) {
-        if (!MigrateClocksToBlueprint.isEnabled) {
-            return
-        }
         nic =
             NotificationIconContainer(context, null).apply {
                 id = nicId
@@ -81,10 +77,6 @@
     }
 
     override fun bindData(constraintLayout: ConstraintLayout) {
-        if (!MigrateClocksToBlueprint.isEnabled) {
-            return
-        }
-
         nicBindingDisposable?.dispose()
         nicBindingDisposable =
             NotificationIconContainerViewBinder.bindWhileAttached(
@@ -98,10 +90,6 @@
     }
 
     override fun applyConstraints(constraintSet: ConstraintSet) {
-        if (!MigrateClocksToBlueprint.isEnabled) {
-            return
-        }
-
         val bottomMargin =
             context.resources.getDimensionPixelSize(R.dimen.keyguard_status_view_bottom_margin)
         val isVisible = rootViewModel.isNotifIconContainerVisible.value
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt
index aa7eb29..e8fce9c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt
@@ -22,7 +22,6 @@
 import android.graphics.Rect
 import android.util.DisplayMetrics
 import android.util.Log
-import android.view.View
 import android.view.WindowManager
 import androidx.annotation.VisibleForTesting
 import androidx.constraintlayout.widget.ConstraintLayout
@@ -32,8 +31,6 @@
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.flags.Flags
-import com.android.systemui.keyguard.KeyguardBottomAreaRefactor
-import com.android.systemui.keyguard.MigrateClocksToBlueprint
 import com.android.systemui.keyguard.shared.model.KeyguardSection
 import com.android.systemui.keyguard.ui.binder.DeviceEntryIconViewBinder
 import com.android.systemui.keyguard.ui.view.DeviceEntryIconView
@@ -76,10 +73,6 @@
     private var disposableHandle: DisposableHandle? = null
 
     override fun addViews(constraintLayout: ConstraintLayout) {
-        if (!KeyguardBottomAreaRefactor.isEnabled && !MigrateClocksToBlueprint.isEnabled) {
-            return
-        }
-
         val view =
             DeviceEntryIconView(
                     context,
@@ -194,38 +187,6 @@
                 sensorRect.left,
             )
         }
-
-        // This is only intended to be here until the KeyguardBottomAreaRefactor flag is enabled
-        // Without this logic, the lock icon location changes but the KeyguardBottomAreaView is not
-        // updated and visible ui layout jank occurs. This is due to AmbientIndicationContainer
-        // being in NPVC and laying out prior to the KeyguardRootView.
-        // Remove when KeyguardBottomAreaRefactor is enabled.
-        if (!KeyguardBottomAreaRefactor.isEnabled) {
-            with(notificationPanelView) {
-                val isUdfpsSupported = deviceEntryIconViewModel.get().isUdfpsSupported.value
-                val bottomAreaViewRight = findViewById<View>(R.id.keyguard_bottom_area)?.right ?: 0
-                findViewById<View>(R.id.ambient_indication_container)?.let {
-                    val (ambientLeft, ambientTop) = it.locationOnScreen
-                    if (isUdfpsSupported) {
-                        // make top of ambient indication view the bottom of the lock icon
-                        it.layout(
-                            ambientLeft,
-                            sensorRect.bottom,
-                            bottomAreaViewRight - ambientLeft,
-                            ambientTop + it.measuredHeight,
-                        )
-                    } else {
-                        // make bottom of ambient indication view the top of the lock icon
-                        it.layout(
-                            ambientLeft,
-                            sensorRect.top - it.measuredHeight,
-                            bottomAreaViewRight - ambientLeft,
-                            sensorRect.top,
-                        )
-                    }
-                }
-            }
-        }
     }
 
     companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSection.kt
index 2d9dac4..5bf56e8 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSection.kt
@@ -21,7 +21,6 @@
 import android.view.ViewGroup
 import androidx.constraintlayout.widget.ConstraintLayout
 import androidx.constraintlayout.widget.ConstraintSet
-import com.android.systemui.keyguard.KeyguardBottomAreaRefactor
 import com.android.systemui.keyguard.shared.model.KeyguardSection
 import com.android.systemui.keyguard.ui.binder.KeyguardIndicationAreaBinder
 import com.android.systemui.keyguard.ui.view.KeyguardIndicationArea
@@ -43,21 +42,17 @@
     private var indicationAreaHandle: DisposableHandle? = null
 
     override fun addViews(constraintLayout: ConstraintLayout) {
-        if (KeyguardBottomAreaRefactor.isEnabled) {
-            val view = KeyguardIndicationArea(context, null)
-            constraintLayout.addView(view)
-        }
+        val view = KeyguardIndicationArea(context, null)
+        constraintLayout.addView(view)
     }
 
     override fun bindData(constraintLayout: ConstraintLayout) {
-        if (KeyguardBottomAreaRefactor.isEnabled) {
-            indicationAreaHandle =
-                KeyguardIndicationAreaBinder.bind(
-                    constraintLayout.requireViewById(R.id.keyguard_indication_area),
-                    keyguardIndicationAreaViewModel,
-                    indicationController,
-                )
-        }
+        indicationAreaHandle =
+            KeyguardIndicationAreaBinder.bind(
+                constraintLayout.requireViewById(R.id.keyguard_indication_area),
+                keyguardIndicationAreaViewModel,
+                indicationController,
+            )
     }
 
     override fun applyConstraints(constraintSet: ConstraintSet) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultSettingsPopupMenuSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultSettingsPopupMenuSection.kt
index 5cd5172..f973ced 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultSettingsPopupMenuSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultSettingsPopupMenuSection.kt
@@ -31,7 +31,6 @@
 import androidx.core.view.isVisible
 import com.android.systemui.animation.view.LaunchableLinearLayout
 import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.keyguard.KeyguardBottomAreaRefactor
 import com.android.systemui.keyguard.shared.model.KeyguardSection
 import com.android.systemui.keyguard.ui.binder.KeyguardSettingsViewBinder
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
@@ -56,9 +55,6 @@
     private var settingsPopupMenuHandle: DisposableHandle? = null
 
     override fun addViews(constraintLayout: ConstraintLayout) {
-        if (!KeyguardBottomAreaRefactor.isEnabled) {
-            return
-        }
         val view =
             LayoutInflater.from(constraintLayout.context)
                 .inflate(R.layout.keyguard_settings_popup_menu, constraintLayout, false)
@@ -71,17 +67,15 @@
     }
 
     override fun bindData(constraintLayout: ConstraintLayout) {
-        if (KeyguardBottomAreaRefactor.isEnabled) {
-            settingsPopupMenuHandle =
-                KeyguardSettingsViewBinder.bind(
-                    constraintLayout.requireViewById<View>(R.id.keyguard_settings_button),
-                    keyguardSettingsMenuViewModel,
-                    keyguardTouchHandlingViewModel,
-                    keyguardRootViewModel,
-                    vibratorHelper,
-                    activityStarter,
-                )
-        }
+        settingsPopupMenuHandle =
+            KeyguardSettingsViewBinder.bind(
+                constraintLayout.requireViewById<View>(R.id.keyguard_settings_button),
+                keyguardSettingsMenuViewModel,
+                keyguardTouchHandlingViewModel,
+                keyguardRootViewModel,
+                vibratorHelper,
+                activityStarter,
+            )
     }
 
     override fun applyConstraints(constraintSet: ConstraintSet) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultShortcutsSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultShortcutsSection.kt
index d3895de..82f142b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultShortcutsSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultShortcutsSection.kt
@@ -28,7 +28,6 @@
 import androidx.constraintlayout.widget.ConstraintSet.VISIBILITY_MODE_IGNORE
 import com.android.systemui.animation.view.LaunchableImageView
 import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.keyguard.KeyguardBottomAreaRefactor
 import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.keyguard.ui.binder.KeyguardQuickAffordanceViewBinder
@@ -49,7 +48,6 @@
     @Named(LOCKSCREEN_INSTANCE)
     private val keyguardQuickAffordancesCombinedViewModel:
         KeyguardQuickAffordancesCombinedViewModel,
-    private val keyguardRootViewModel: KeyguardRootViewModel,
     private val indicationController: KeyguardIndicationController,
     private val keyguardBlueprintInteractor: Lazy<KeyguardBlueprintInteractor>,
     private val keyguardQuickAffordanceViewBinder: KeyguardQuickAffordanceViewBinder,
@@ -60,46 +58,42 @@
     private var safeInsetBottom = 0
 
     override fun addViews(constraintLayout: ConstraintLayout) {
-        if (KeyguardBottomAreaRefactor.isEnabled) {
-            addLeftShortcut(constraintLayout)
-            addRightShortcut(constraintLayout)
+        addLeftShortcut(constraintLayout)
+        addRightShortcut(constraintLayout)
 
-            constraintLayout
-                .requireViewById<LaunchableImageView>(R.id.start_button)
-                .setOnApplyWindowInsetsListener { _, windowInsets ->
-                    val tempSafeInset = windowInsets?.displayCutout?.safeInsetBottom ?: 0
-                    if (safeInsetBottom != tempSafeInset) {
-                        safeInsetBottom = tempSafeInset
-                        keyguardBlueprintInteractor
-                            .get()
-                            .refreshBlueprint(IntraBlueprintTransition.Type.DefaultTransition)
-                    }
-                    WindowInsets.CONSUMED
+        constraintLayout
+            .requireViewById<LaunchableImageView>(R.id.start_button)
+            .setOnApplyWindowInsetsListener { _, windowInsets ->
+                val tempSafeInset = windowInsets?.displayCutout?.safeInsetBottom ?: 0
+                if (safeInsetBottom != tempSafeInset) {
+                    safeInsetBottom = tempSafeInset
+                    keyguardBlueprintInteractor
+                        .get()
+                        .refreshBlueprint(IntraBlueprintTransition.Type.DefaultTransition)
                 }
-        }
+                WindowInsets.CONSUMED
+            }
     }
 
     override fun bindData(constraintLayout: ConstraintLayout) {
-        if (KeyguardBottomAreaRefactor.isEnabled) {
-            leftShortcutHandle?.destroy()
-            leftShortcutHandle =
-                keyguardQuickAffordanceViewBinder.bind(
-                    constraintLayout.requireViewById(R.id.start_button),
-                    keyguardQuickAffordancesCombinedViewModel.startButton,
-                    keyguardQuickAffordancesCombinedViewModel.transitionAlpha,
-                ) {
-                    indicationController.showTransientIndication(it)
-                }
-            rightShortcutHandle?.destroy()
-            rightShortcutHandle =
-                keyguardQuickAffordanceViewBinder.bind(
-                    constraintLayout.requireViewById(R.id.end_button),
-                    keyguardQuickAffordancesCombinedViewModel.endButton,
-                    keyguardQuickAffordancesCombinedViewModel.transitionAlpha,
-                ) {
-                    indicationController.showTransientIndication(it)
-                }
-        }
+        leftShortcutHandle?.destroy()
+        leftShortcutHandle =
+            keyguardQuickAffordanceViewBinder.bind(
+                constraintLayout.requireViewById(R.id.start_button),
+                keyguardQuickAffordancesCombinedViewModel.startButton,
+                keyguardQuickAffordancesCombinedViewModel.transitionAlpha,
+            ) {
+                indicationController.showTransientIndication(it)
+            }
+        rightShortcutHandle?.destroy()
+        rightShortcutHandle =
+            keyguardQuickAffordanceViewBinder.bind(
+                constraintLayout.requireViewById(R.id.end_button),
+                keyguardQuickAffordancesCombinedViewModel.endButton,
+                keyguardQuickAffordancesCombinedViewModel.transitionAlpha,
+            ) {
+                indicationController.showTransientIndication(it)
+            }
     }
 
     override fun applyConstraints(constraintSet: ConstraintSet) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultUdfpsAccessibilityOverlaySection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultUdfpsAccessibilityOverlaySection.kt
index 0ae1400..8186aa3 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultUdfpsAccessibilityOverlaySection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultUdfpsAccessibilityOverlaySection.kt
@@ -23,7 +23,6 @@
 import com.android.systemui.deviceentry.ui.binder.UdfpsAccessibilityOverlayBinder
 import com.android.systemui.deviceentry.ui.view.UdfpsAccessibilityOverlay
 import com.android.systemui.deviceentry.ui.viewmodel.DeviceEntryUdfpsAccessibilityOverlayViewModel
-import com.android.systemui.keyguard.KeyguardBottomAreaRefactor
 import com.android.systemui.keyguard.shared.model.KeyguardSection
 import com.android.systemui.res.R
 import com.android.systemui.shade.ShadeDisplayAware
@@ -67,16 +66,12 @@
                 ConstraintSet.BOTTOM,
             )
 
-            if (KeyguardBottomAreaRefactor.isEnabled) {
-                connect(
-                    viewId,
-                    ConstraintSet.BOTTOM,
-                    R.id.keyguard_indication_area,
-                    ConstraintSet.TOP,
-                )
-            } else {
-                connect(viewId, ConstraintSet.BOTTOM, ConstraintSet.PARENT_ID, ConstraintSet.BOTTOM)
-            }
+            connect(
+                viewId,
+                ConstraintSet.BOTTOM,
+                R.id.keyguard_indication_area,
+                ConstraintSet.TOP,
+            )
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToPrimaryBouncerTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToPrimaryBouncerTransitionViewModel.kt
index 85ce5cd..8af5b5f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToPrimaryBouncerTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToPrimaryBouncerTransitionViewModel.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.keyguard.ui.viewmodel
 
+import android.util.MathUtils
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.domain.interactor.FromAlternateBouncerTransitionInteractor
 import com.android.systemui.keyguard.shared.model.Edge
@@ -23,12 +24,17 @@
 import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition
+import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition.Companion.MAX_BACKGROUND_BLUR_RADIUS
+import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition.Companion.MIN_BACKGROUND_BLUR_RADIUS
 import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.scene.ui.composable.transitions.TO_BOUNCER_FADE_FRACTION
+import com.android.systemui.window.flag.WindowBlurFlag
 import javax.inject.Inject
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.emptyFlow
 
 /**
  * Breaks down ALTERNATE BOUNCER->PRIMARY BOUNCER transition into discrete steps for corresponding
@@ -38,7 +44,10 @@
 @SysUISingleton
 class AlternateBouncerToPrimaryBouncerTransitionViewModel
 @Inject
-constructor(animationFlow: KeyguardTransitionAnimationFlow) : DeviceEntryIconTransition {
+constructor(
+    animationFlow: KeyguardTransitionAnimationFlow,
+    shadeDependentFlows: ShadeDependentFlows,
+) : DeviceEntryIconTransition, PrimaryBouncerTransition {
     private val transitionAnimation =
         animationFlow
             .setup(
@@ -57,12 +66,30 @@
             else -> { step -> 1f - step }
         }
 
-    val lockscreenAlpha: Flow<Float> =
+    private val alphaFlow =
         transitionAnimation.sharedFlow(
             duration = FromAlternateBouncerTransitionInteractor.TO_PRIMARY_BOUNCER_DURATION,
             onStep = alphaForAnimationStep,
         )
 
+    val lockscreenAlpha: Flow<Float> = if (WindowBlurFlag.isEnabled) alphaFlow else emptyFlow()
+
+    val notificationAlpha: Flow<Float> = alphaFlow
+
     override val deviceEntryParentViewAlpha: Flow<Float> =
         transitionAnimation.immediatelyTransitionTo(0f)
+
+    override val windowBlurRadius: Flow<Float> =
+        shadeDependentFlows.transitionFlow(
+            flowWhenShadeIsExpanded =
+                transitionAnimation.immediatelyTransitionTo(MAX_BACKGROUND_BLUR_RADIUS),
+            flowWhenShadeIsNotExpanded =
+                transitionAnimation.sharedFlow(
+                    duration = FromAlternateBouncerTransitionInteractor.TO_PRIMARY_BOUNCER_DURATION,
+                    onStep = { step ->
+                        MathUtils.lerp(MIN_BACKGROUND_BLUR_RADIUS, MAX_BACKGROUND_BLUR_RADIUS, step)
+                    },
+                    onFinish = { MAX_BACKGROUND_BLUR_RADIUS },
+                ),
+        )
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToPrimaryBouncerTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToPrimaryBouncerTransitionViewModel.kt
index 35f05f5..e6b796f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToPrimaryBouncerTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToPrimaryBouncerTransitionViewModel.kt
@@ -23,6 +23,8 @@
 import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition
+import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition.Companion.MAX_BACKGROUND_BLUR_RADIUS
 import com.android.systemui.scene.shared.model.Scenes
 import javax.inject.Inject
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -36,19 +38,19 @@
 @SysUISingleton
 class AodToPrimaryBouncerTransitionViewModel
 @Inject
-constructor(
-    animationFlow: KeyguardTransitionAnimationFlow,
-) : DeviceEntryIconTransition {
+constructor(animationFlow: KeyguardTransitionAnimationFlow) :
+    DeviceEntryIconTransition, PrimaryBouncerTransition {
     private val transitionAnimation =
         animationFlow
             .setup(
                 duration = FromAodTransitionInteractor.TO_PRIMARY_BOUNCER_DURATION,
                 edge = Edge.create(from = AOD, to = Scenes.Bouncer),
             )
-            .setupWithoutSceneContainer(
-                edge = Edge.create(from = AOD, to = PRIMARY_BOUNCER),
-            )
+            .setupWithoutSceneContainer(edge = Edge.create(from = AOD, to = PRIMARY_BOUNCER))
 
     override val deviceEntryParentViewAlpha: Flow<Float> =
         transitionAnimation.immediatelyTransitionTo(0f)
+
+    override val windowBlurRadius: Flow<Float> =
+        transitionAnimation.immediatelyTransitionTo(MAX_BACKGROUND_BLUR_RADIUS)
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToPrimaryBouncerTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToPrimaryBouncerTransitionViewModel.kt
index 7ddf641..c1670c3 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToPrimaryBouncerTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToPrimaryBouncerTransitionViewModel.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.keyguard.ui.viewmodel
 
+import android.util.MathUtils
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.domain.interactor.FromDozingTransitionInteractor.Companion.TO_PRIMARY_BOUNCER_DURATION
 import com.android.systemui.keyguard.shared.model.Edge
@@ -23,6 +24,9 @@
 import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition
+import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition.Companion.MAX_BACKGROUND_BLUR_RADIUS
+import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition.Companion.MIN_BACKGROUND_BLUR_RADIUS
 import com.android.systemui.scene.shared.model.Scenes
 import javax.inject.Inject
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -36,9 +40,8 @@
 @SysUISingleton
 class DozingToPrimaryBouncerTransitionViewModel
 @Inject
-constructor(
-    animationFlow: KeyguardTransitionAnimationFlow,
-) : DeviceEntryIconTransition {
+constructor(animationFlow: KeyguardTransitionAnimationFlow) :
+    DeviceEntryIconTransition, PrimaryBouncerTransition {
 
     private val transitionAnimation =
         animationFlow
@@ -46,10 +49,17 @@
                 duration = TO_PRIMARY_BOUNCER_DURATION,
                 edge = Edge.create(from = DOZING, to = Scenes.Bouncer),
             )
-            .setupWithoutSceneContainer(
-                edge = Edge.create(from = DOZING, to = PRIMARY_BOUNCER),
-            )
+            .setupWithoutSceneContainer(edge = Edge.create(from = DOZING, to = PRIMARY_BOUNCER))
 
     override val deviceEntryParentViewAlpha: Flow<Float> =
         transitionAnimation.immediatelyTransitionTo(0f)
+
+    override val windowBlurRadius: Flow<Float> =
+        transitionAnimation.sharedFlow(
+            TO_PRIMARY_BOUNCER_DURATION,
+            onStep = { step ->
+                MathUtils.lerp(MIN_BACKGROUND_BLUR_RADIUS, MAX_BACKGROUND_BLUR_RADIUS, step)
+            },
+            onFinish = { MAX_BACKGROUND_BLUR_RADIUS },
+        )
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt
deleted file mode 100644
index 6fe51ae..0000000
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt
+++ /dev/null
@@ -1,252 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.keyguard.ui.viewmodel
-
-import androidx.annotation.VisibleForTesting
-import com.android.systemui.doze.util.BurnInHelperWrapper
-import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor
-import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
-import com.android.systemui.keyguard.domain.interactor.KeyguardQuickAffordanceInteractor
-import com.android.systemui.keyguard.domain.model.KeyguardQuickAffordanceModel
-import com.android.systemui.keyguard.shared.quickaffordance.ActivationState
-import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition
-import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
-import javax.inject.Inject
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.flatMapLatest
-import kotlinx.coroutines.flow.flowOf
-import kotlinx.coroutines.flow.map
-
-/** View-model for the keyguard bottom area view */
-@OptIn(ExperimentalCoroutinesApi::class)
-class KeyguardBottomAreaViewModel
-@Inject
-constructor(
-    private val keyguardInteractor: KeyguardInteractor,
-    private val quickAffordanceInteractor: KeyguardQuickAffordanceInteractor,
-    private val bottomAreaInteractor: KeyguardBottomAreaInteractor,
-    private val burnInHelperWrapper: BurnInHelperWrapper,
-    private val keyguardTouchHandlingViewModel: KeyguardTouchHandlingViewModel,
-    val settingsMenuViewModel: KeyguardSettingsMenuViewModel,
-) {
-    data class PreviewMode(
-        val isInPreviewMode: Boolean = false,
-        val shouldHighlightSelectedAffordance: Boolean = false,
-    )
-
-    /**
-     * Whether this view-model instance is powering the preview experience that renders exclusively
-     * in the wallpaper picker application. This should _always_ be `false` for the real lock screen
-     * experience.
-     */
-    val previewMode = MutableStateFlow(PreviewMode())
-
-    /**
-     * ID of the slot that's currently selected in the preview that renders exclusively in the
-     * wallpaper picker application. This is ignored for the actual, real lock screen experience.
-     */
-    private val selectedPreviewSlotId =
-        MutableStateFlow(KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START)
-
-    /**
-     * Whether quick affordances are "opaque enough" to be considered visible to and interactive by
-     * the user. If they are not interactive, user input should not be allowed on them.
-     *
-     * Note that there is a margin of error, where we allow very, very slightly transparent views to
-     * be considered "fully opaque" for the purpose of being interactive. This is to accommodate the
-     * error margin of floating point arithmetic.
-     *
-     * A view that is visible but with an alpha of less than our threshold either means it's not
-     * fully done fading in or is fading/faded out. Either way, it should not be
-     * interactive/clickable unless "fully opaque" to avoid issues like in b/241830987.
-     */
-    private val areQuickAffordancesFullyOpaque: Flow<Boolean> =
-        bottomAreaInteractor.alpha
-            .map { alpha -> alpha >= AFFORDANCE_FULLY_OPAQUE_ALPHA_THRESHOLD }
-            .distinctUntilChanged()
-
-    /** An observable for the view-model of the "start button" quick affordance. */
-    val startButton: Flow<KeyguardQuickAffordanceViewModel> =
-        button(KeyguardQuickAffordancePosition.BOTTOM_START)
-    /** An observable for the view-model of the "end button" quick affordance. */
-    val endButton: Flow<KeyguardQuickAffordanceViewModel> =
-        button(KeyguardQuickAffordancePosition.BOTTOM_END)
-    /** An observable for whether the overlay container should be visible. */
-    val isOverlayContainerVisible: Flow<Boolean> =
-        keyguardInteractor.isDozing.map { !it }.distinctUntilChanged()
-    /** An observable for the alpha level for the entire bottom area. */
-    val alpha: Flow<Float> =
-        previewMode.flatMapLatest {
-            if (it.isInPreviewMode) {
-                flowOf(1f)
-            } else {
-                bottomAreaInteractor.alpha.distinctUntilChanged()
-            }
-        }
-    /** An observable for the x-offset by which the indication area should be translated. */
-    val indicationAreaTranslationX: Flow<Float> =
-        bottomAreaInteractor.clockPosition.map { it.x.toFloat() }.distinctUntilChanged()
-
-    /** Returns an observable for the y-offset by which the indication area should be translated. */
-    fun indicationAreaTranslationY(defaultBurnInOffset: Int): Flow<Float> {
-        return keyguardInteractor.dozeAmount
-            .map { dozeAmount ->
-                dozeAmount *
-                    (burnInHelperWrapper.burnInOffset(
-                        /* amplitude = */ defaultBurnInOffset * 2,
-                        /* xAxis= */ false,
-                    ) - defaultBurnInOffset)
-            }
-            .distinctUntilChanged()
-    }
-
-    /**
-     * Returns whether the keyguard bottom area should be constrained to the top of the lock icon
-     */
-    fun shouldConstrainToTopOfLockIcon(): Boolean =
-        bottomAreaInteractor.shouldConstrainToTopOfLockIcon()
-
-    /**
-     * Puts this view-model in "preview mode", which means it's being used for UI that is rendering
-     * the lock screen preview in wallpaper picker / settings and not the real experience on the
-     * lock screen.
-     *
-     * @param initiallySelectedSlotId The ID of the initial slot to render as the selected one.
-     * @param shouldHighlightSelectedAffordance Whether the selected quick affordance should be
-     *   highlighted (while all others are dimmed to make the selected one stand out).
-     */
-    fun enablePreviewMode(
-        initiallySelectedSlotId: String?,
-        shouldHighlightSelectedAffordance: Boolean,
-    ) {
-        previewMode.value =
-            PreviewMode(
-                isInPreviewMode = true,
-                shouldHighlightSelectedAffordance = shouldHighlightSelectedAffordance,
-            )
-        onPreviewSlotSelected(
-            initiallySelectedSlotId ?: KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START
-        )
-    }
-
-    /**
-     * Notifies that a slot with the given ID has been selected in the preview experience that is
-     * rendering in the wallpaper picker. This is ignored for the real lock screen experience.
-     *
-     * @see enablePreviewMode
-     */
-    fun onPreviewSlotSelected(slotId: String) {
-        selectedPreviewSlotId.value = slotId
-    }
-
-    /**
-     * Notifies that some input gesture has started somewhere in the bottom area that's outside of
-     * the lock screen settings menu item pop-up.
-     */
-    fun onTouchedOutsideLockScreenSettingsMenu() {
-        keyguardTouchHandlingViewModel.onTouchedOutside()
-    }
-
-    private fun button(
-        position: KeyguardQuickAffordancePosition
-    ): Flow<KeyguardQuickAffordanceViewModel> {
-        return previewMode.flatMapLatest { previewMode ->
-            combine(
-                    if (previewMode.isInPreviewMode) {
-                        quickAffordanceInteractor.quickAffordanceAlwaysVisible(position = position)
-                    } else {
-                        quickAffordanceInteractor.quickAffordance(position = position)
-                    },
-                    bottomAreaInteractor.animateDozingTransitions.distinctUntilChanged(),
-                    areQuickAffordancesFullyOpaque,
-                    selectedPreviewSlotId,
-                    quickAffordanceInteractor.useLongPress(),
-                ) { model, animateReveal, isFullyOpaque, selectedPreviewSlotId, useLongPress ->
-                    val slotId = position.toSlotId()
-                    val isSelected = selectedPreviewSlotId == slotId
-                    model.toViewModel(
-                        animateReveal = !previewMode.isInPreviewMode && animateReveal,
-                        isClickable = isFullyOpaque && !previewMode.isInPreviewMode,
-                        isSelected =
-                            previewMode.isInPreviewMode &&
-                                previewMode.shouldHighlightSelectedAffordance &&
-                                isSelected,
-                        isDimmed =
-                            previewMode.isInPreviewMode &&
-                                previewMode.shouldHighlightSelectedAffordance &&
-                                !isSelected,
-                        forceInactive = previewMode.isInPreviewMode,
-                        slotId = slotId,
-                        useLongPress = useLongPress,
-                    )
-                }
-                .distinctUntilChanged()
-        }
-    }
-
-    private fun KeyguardQuickAffordanceModel.toViewModel(
-        animateReveal: Boolean,
-        isClickable: Boolean,
-        isSelected: Boolean,
-        isDimmed: Boolean,
-        forceInactive: Boolean,
-        slotId: String,
-        useLongPress: Boolean,
-    ): KeyguardQuickAffordanceViewModel {
-        return when (this) {
-            is KeyguardQuickAffordanceModel.Visible ->
-                KeyguardQuickAffordanceViewModel(
-                    configKey = configKey,
-                    isVisible = true,
-                    animateReveal = animateReveal,
-                    icon = icon,
-                    onClicked = { parameters ->
-                        quickAffordanceInteractor.onQuickAffordanceTriggered(
-                            configKey = parameters.configKey,
-                            expandable = parameters.expandable,
-                            slotId = parameters.slotId,
-                        )
-                    },
-                    isClickable = isClickable,
-                    isActivated = !forceInactive && activationState is ActivationState.Active,
-                    isSelected = isSelected,
-                    useLongPress = useLongPress,
-                    isDimmed = isDimmed,
-                    slotId = slotId,
-                )
-            is KeyguardQuickAffordanceModel.Hidden ->
-                KeyguardQuickAffordanceViewModel(
-                    slotId = slotId,
-                )
-        }
-    }
-
-    companion object {
-        // We select a value that's less than 1.0 because we want floating point math precision to
-        // not be a factor in determining whether the affordance UI is fully opaque. The number we
-        // choose needs to be close enough 1.0 such that the user can't easily tell the difference
-        // between the UI with an alpha at the threshold and when the alpha is 1.0. At the same
-        // time, we don't want the number to be too close to 1.0 such that there is a chance that we
-        // never treat the affordance UI as "fully opaque" as that would risk making it forever not
-        // clickable.
-        @VisibleForTesting const val AFFORDANCE_FULLY_OPAQUE_ALPHA_THRESHOLD = 0.95f
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModel.kt
index bc3ef02..4663a2b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModel.kt
@@ -21,10 +21,8 @@
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.doze.util.BurnInHelperWrapper
-import com.android.systemui.keyguard.KeyguardBottomAreaRefactor
 import com.android.systemui.keyguard.MigrateClocksToBlueprint
 import com.android.systemui.keyguard.domain.interactor.BurnInInteractor
-import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
 import com.android.systemui.keyguard.shared.model.BurnInModel
@@ -48,8 +46,6 @@
 @Inject
 constructor(
     private val keyguardInteractor: KeyguardInteractor,
-    bottomAreaInteractor: KeyguardBottomAreaInteractor,
-    keyguardBottomAreaViewModel: KeyguardBottomAreaViewModel,
     private val burnInHelperWrapper: BurnInHelperWrapper,
     burnInInteractor: BurnInInteractor,
     @Named(KeyguardQuickAffordancesCombinedViewModelModule.Companion.LOCKSCREEN_INSTANCE)
@@ -64,9 +60,6 @@
     /** Notifies when a new configuration is set */
     val configurationChange: Flow<Unit> = configurationInteractor.onAnyConfigurationChange
 
-    /** An observable for the alpha level for the entire bottom area. */
-    val alpha: Flow<Float> = keyguardBottomAreaViewModel.alpha
-
     /** An observable for the visibility value for the indication area view. */
     val visible: Flow<Boolean> =
         anyOf(
@@ -76,22 +69,12 @@
 
     /** An observable for whether the indication area should be padded. */
     val isIndicationAreaPadded: Flow<Boolean> =
-        if (KeyguardBottomAreaRefactor.isEnabled) {
-            combine(shortcutsCombinedViewModel.startButton, shortcutsCombinedViewModel.endButton) {
-                    startButtonModel,
-                    endButtonModel ->
-                    startButtonModel.isVisible || endButtonModel.isVisible
-                }
-                .distinctUntilChanged()
-        } else {
-            combine(
-                    keyguardBottomAreaViewModel.startButton,
-                    keyguardBottomAreaViewModel.endButton,
-                ) { startButtonModel, endButtonModel ->
-                    startButtonModel.isVisible || endButtonModel.isVisible
-                }
-                .distinctUntilChanged()
+        combine(shortcutsCombinedViewModel.startButton, shortcutsCombinedViewModel.endButton) {
+                startButtonModel,
+                endButtonModel ->
+            startButtonModel.isVisible || endButtonModel.isVisible
         }
+            .distinctUntilChanged()
 
     @OptIn(ExperimentalCoroutinesApi::class)
     private val burnIn: Flow<BurnInModel> =
@@ -114,11 +97,7 @@
 
     /** An observable for the x-offset by which the indication area should be translated. */
     val indicationAreaTranslationX: Flow<Float> =
-        if (MigrateClocksToBlueprint.isEnabled || KeyguardBottomAreaRefactor.isEnabled) {
-            burnIn.map { it.translationX.toFloat() }.flowOn(mainDispatcher)
-        } else {
-            bottomAreaInteractor.clockPosition.map { it.x.toFloat() }.distinctUntilChanged()
-        }
+        burnIn.map { it.translationX.toFloat() }.flowOn(mainDispatcher)
 
     /** Returns an observable for the y-offset by which the indication area should be translated. */
     fun indicationAreaTranslationY(defaultBurnInOffset: Int): Flow<Float> {
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 0d81604..9066d46 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
@@ -92,6 +92,8 @@
         AlternateBouncerToLockscreenTransitionViewModel,
     private val alternateBouncerToOccludedTransitionViewModel:
         AlternateBouncerToOccludedTransitionViewModel,
+    private val alternateBouncerToPrimaryBouncerTransitionViewModel:
+        AlternateBouncerToPrimaryBouncerTransitionViewModel,
     private val aodToGoneTransitionViewModel: AodToGoneTransitionViewModel,
     private val aodToLockscreenTransitionViewModel: AodToLockscreenTransitionViewModel,
     private val aodToOccludedTransitionViewModel: AodToOccludedTransitionViewModel,
@@ -238,6 +240,7 @@
                         alternateBouncerToAodTransitionViewModel.lockscreenAlpha(viewState),
                         alternateBouncerToGoneTransitionViewModel.lockscreenAlpha(viewState),
                         alternateBouncerToLockscreenTransitionViewModel.lockscreenAlpha(viewState),
+                        alternateBouncerToPrimaryBouncerTransitionViewModel.lockscreenAlpha,
                         alternateBouncerToOccludedTransitionViewModel.lockscreenAlpha,
                         aodToGoneTransitionViewModel.lockscreenAlpha(viewState),
                         aodToLockscreenTransitionViewModel.lockscreenAlpha(viewState),
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt
index 914730e..48cc8ad 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.keyguard.ui.viewmodel
 
+import android.util.MathUtils
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor
 import com.android.systemui.keyguard.shared.model.Edge
@@ -23,6 +24,9 @@
 import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition
+import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition.Companion.MAX_BACKGROUND_BLUR_RADIUS
+import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition.Companion.MIN_BACKGROUND_BLUR_RADIUS
 import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.scene.ui.composable.transitions.TO_BOUNCER_FADE_FRACTION
@@ -42,7 +46,7 @@
 constructor(
     shadeDependentFlows: ShadeDependentFlows,
     animationFlow: KeyguardTransitionAnimationFlow,
-) : DeviceEntryIconTransition {
+) : DeviceEntryIconTransition, PrimaryBouncerTransition {
     private val transitionAnimation =
         animationFlow
             .setup(
@@ -78,4 +82,16 @@
                 ),
             flowWhenShadeIsExpanded = transitionAnimation.immediatelyTransitionTo(0f),
         )
+    override val windowBlurRadius: Flow<Float> =
+        shadeDependentFlows.transitionFlow(
+            flowWhenShadeIsExpanded =
+                transitionAnimation.immediatelyTransitionTo(MAX_BACKGROUND_BLUR_RADIUS),
+            flowWhenShadeIsNotExpanded =
+                transitionAnimation.sharedFlow(
+                    duration = FromLockscreenTransitionInteractor.TO_PRIMARY_BOUNCER_DURATION,
+                    onStep = {
+                        MathUtils.lerp(MIN_BACKGROUND_BLUR_RADIUS, MAX_BACKGROUND_BLUR_RADIUS, it)
+                    },
+                ),
+        )
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModel.kt
index 501feca..f14144e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModel.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.keyguard.ui.viewmodel
 
+import android.util.MathUtils
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
 import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor
@@ -24,6 +25,9 @@
 import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition
+import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition.Companion.MAX_BACKGROUND_BLUR_RADIUS
+import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition.Companion.MIN_BACKGROUND_BLUR_RADIUS
 import com.android.systemui.scene.shared.model.Scenes
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
@@ -43,16 +47,14 @@
 constructor(
     deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor,
     animationFlow: KeyguardTransitionAnimationFlow,
-) : DeviceEntryIconTransition {
+) : DeviceEntryIconTransition, PrimaryBouncerTransition {
     private val transitionAnimation =
         animationFlow
             .setup(
                 duration = FromPrimaryBouncerTransitionInteractor.TO_AOD_DURATION,
                 edge = Edge.create(from = Scenes.Bouncer, to = AOD),
             )
-            .setupWithoutSceneContainer(
-                edge = Edge.create(from = PRIMARY_BOUNCER, to = AOD),
-            )
+            .setupWithoutSceneContainer(edge = Edge.create(from = PRIMARY_BOUNCER, to = AOD))
 
     val deviceEntryBackgroundViewAlpha: Flow<Float> =
         transitionAnimation.immediatelyTransitionTo(0f)
@@ -60,7 +62,7 @@
     val lockscreenAlpha: Flow<Float> =
         transitionAnimation.sharedFlow(
             duration = FromPrimaryBouncerTransitionInteractor.TO_AOD_DURATION,
-            onStep = { it }
+            onStep = { it },
         )
 
     override val deviceEntryParentViewAlpha: Flow<Float> =
@@ -77,4 +79,13 @@
                 emptyFlow()
             }
         }
+
+    override val windowBlurRadius: Flow<Float> =
+        transitionAnimation.sharedFlow(
+            duration = FromPrimaryBouncerTransitionInteractor.TO_AOD_DURATION,
+            onStep = { step ->
+                MathUtils.lerp(MAX_BACKGROUND_BLUR_RADIUS, MIN_BACKGROUND_BLUR_RADIUS, step)
+            },
+            onFinish = { MIN_BACKGROUND_BLUR_RADIUS },
+        )
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToDozingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToDozingTransitionViewModel.kt
index e5bb464..a24ed26 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToDozingTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToDozingTransitionViewModel.kt
@@ -24,6 +24,7 @@
 import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition
 import com.android.systemui.scene.shared.model.Scenes
 import javax.inject.Inject
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -42,7 +43,7 @@
 constructor(
     deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor,
     animationFlow: KeyguardTransitionAnimationFlow,
-) : DeviceEntryIconTransition {
+) : DeviceEntryIconTransition, PrimaryBouncerTransition {
 
     private val transitionAnimation =
         animationFlow
@@ -50,9 +51,7 @@
                 duration = TO_DOZING_DURATION,
                 edge = Edge.create(from = Scenes.Bouncer, to = DOZING),
             )
-            .setupWithoutSceneContainer(
-                edge = Edge.create(from = PRIMARY_BOUNCER, to = DOZING),
-            )
+            .setupWithoutSceneContainer(edge = Edge.create(from = PRIMARY_BOUNCER, to = DOZING))
 
     val deviceEntryBackgroundViewAlpha: Flow<Float> =
         transitionAnimation.immediatelyTransitionTo(0f)
@@ -66,4 +65,9 @@
                 emptyFlow()
             }
         }
+
+    override val windowBlurRadius: Flow<Float> =
+        transitionAnimation.immediatelyTransitionTo(
+            PrimaryBouncerTransition.MIN_BACKGROUND_BLUR_RADIUS
+        )
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGlanceableHubTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGlanceableHubTransitionViewModel.kt
index 9ec15dc..b52a390 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGlanceableHubTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGlanceableHubTransitionViewModel.kt
@@ -23,13 +23,16 @@
 import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition
+import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition.Companion.MIN_BACKGROUND_BLUR_RADIUS
 import javax.inject.Inject
 import kotlinx.coroutines.flow.Flow
 
 @SysUISingleton
 class PrimaryBouncerToGlanceableHubTransitionViewModel
 @Inject
-constructor(animationFlow: KeyguardTransitionAnimationFlow) : DeviceEntryIconTransition {
+constructor(animationFlow: KeyguardTransitionAnimationFlow) :
+    DeviceEntryIconTransition, PrimaryBouncerTransition {
     private val transitionAnimation =
         animationFlow
             .setup(duration = TO_GLANCEABLE_HUB_DURATION, edge = Edge.INVALID)
@@ -37,4 +40,7 @@
 
     override val deviceEntryParentViewAlpha: Flow<Float> =
         transitionAnimation.immediatelyTransitionTo(1f)
+
+    override val windowBlurRadius: Flow<Float> =
+        transitionAnimation.immediatelyTransitionTo(MIN_BACKGROUND_BLUR_RADIUS)
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt
index 17c678e..713ac15 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt
@@ -16,16 +16,21 @@
 
 package com.android.systemui.keyguard.ui.viewmodel
 
+import android.util.MathUtils
 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.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor.Companion.TO_GONE_DURATION
+import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor.Companion.TO_GONE_SHORT_DURATION
 import com.android.systemui.keyguard.domain.interactor.KeyguardDismissActionInteractor
 import com.android.systemui.keyguard.shared.model.Edge
 import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
 import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
 import com.android.systemui.keyguard.shared.model.ScrimAlpha
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition
+import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition.Companion.MAX_BACKGROUND_BLUR_RADIUS
+import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition.Companion.MIN_BACKGROUND_BLUR_RADIUS
 import com.android.systemui.statusbar.SysuiStatusBarStateController
 import dagger.Lazy
 import javax.inject.Inject
@@ -48,16 +53,11 @@
     keyguardDismissActionInteractor: Lazy<KeyguardDismissActionInteractor>,
     bouncerToGoneFlows: BouncerToGoneFlows,
     animationFlow: KeyguardTransitionAnimationFlow,
-) {
+) : PrimaryBouncerTransition {
     private val transitionAnimation =
         animationFlow
-            .setup(
-                duration = TO_GONE_DURATION,
-                edge = Edge.INVALID,
-            )
-            .setupWithoutSceneContainer(
-                edge = Edge.create(from = PRIMARY_BOUNCER, to = GONE),
-            )
+            .setup(duration = TO_GONE_DURATION, edge = Edge.INVALID)
+            .setupWithoutSceneContainer(edge = Edge.create(from = PRIMARY_BOUNCER, to = GONE))
 
     private var leaveShadeOpen: Boolean = false
     private var willRunDismissFromKeyguard: Boolean = false
@@ -96,7 +96,7 @@
 
     private fun createBouncerAlphaFlow(willRunAnimationOnKeyguard: () -> Boolean): Flow<Float> {
         return transitionAnimation.sharedFlow(
-            duration = 200.milliseconds,
+            duration = TO_GONE_SHORT_DURATION,
             onStart = { willRunDismissFromKeyguard = willRunAnimationOnKeyguard() },
             onStep = {
                 if (willRunDismissFromKeyguard) {
@@ -108,6 +108,22 @@
         )
     }
 
+    private fun createBouncerWindowBlurFlow(
+        willRunAnimationOnKeyguard: () -> Boolean
+    ): Flow<Float> {
+        return transitionAnimation.sharedFlow(
+            duration = TO_GONE_SHORT_DURATION,
+            onStart = { willRunDismissFromKeyguard = willRunAnimationOnKeyguard() },
+            onStep = {
+                if (willRunDismissFromKeyguard) {
+                    MIN_BACKGROUND_BLUR_RADIUS
+                } else {
+                    MathUtils.lerp(MAX_BACKGROUND_BLUR_RADIUS, MIN_BACKGROUND_BLUR_RADIUS, it)
+                }
+            },
+        )
+    }
+
     /** Lockscreen alpha */
     val lockscreenAlpha: Flow<Float> =
         if (ComposeBouncerFlags.isEnabled) {
@@ -137,6 +153,16 @@
         )
     }
 
+    override val windowBlurRadius: Flow<Float> =
+        if (ComposeBouncerFlags.isEnabled) {
+            keyguardDismissActionInteractor
+                .get()
+                .willAnimateDismissActionOnLockscreen
+                .flatMapLatest { createBouncerWindowBlurFlow { it } }
+        } else {
+            createBouncerWindowBlurFlow(primaryBouncerInteractor::willRunDismissFromKeyguard)
+        }
+
     val scrimAlpha: Flow<ScrimAlpha> =
         bouncerToGoneFlows.scrimAlpha(TO_GONE_DURATION, PRIMARY_BOUNCER)
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModel.kt
index d29f512..e737fce 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModel.kt
@@ -25,6 +25,9 @@
 import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition
+import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition.Companion.MAX_BACKGROUND_BLUR_RADIUS
+import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition.Companion.MIN_BACKGROUND_BLUR_RADIUS
 import com.android.systemui.scene.shared.model.Scenes
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
@@ -41,22 +44,21 @@
 @Inject
 constructor(
     animationFlow: KeyguardTransitionAnimationFlow,
-) : DeviceEntryIconTransition {
+    shadeDependentFlows: ShadeDependentFlows,
+) : DeviceEntryIconTransition, PrimaryBouncerTransition {
     private val transitionAnimation =
         animationFlow
             .setup(
                 duration = FromPrimaryBouncerTransitionInteractor.TO_LOCKSCREEN_DURATION,
                 edge = Edge.create(from = Scenes.Bouncer, to = LOCKSCREEN),
             )
-            .setupWithoutSceneContainer(
-                edge = Edge.create(from = PRIMARY_BOUNCER, to = LOCKSCREEN),
-            )
+            .setupWithoutSceneContainer(edge = Edge.create(from = PRIMARY_BOUNCER, to = LOCKSCREEN))
 
     val shortcutsAlpha: Flow<Float> =
         transitionAnimation.sharedFlow(
             duration = 250.milliseconds,
             interpolator = EMPHASIZED_ACCELERATE,
-            onStep = { it }
+            onStep = { it },
         )
 
     fun lockscreenAlpha(viewState: ViewStateAccessor): Flow<Float> {
@@ -72,4 +74,17 @@
         transitionAnimation.immediatelyTransitionTo(1f)
     override val deviceEntryParentViewAlpha: Flow<Float> =
         transitionAnimation.immediatelyTransitionTo(1f)
+
+    override val windowBlurRadius: Flow<Float> =
+        shadeDependentFlows.transitionFlow(
+            flowWhenShadeIsExpanded =
+                transitionAnimation.immediatelyTransitionTo(MAX_BACKGROUND_BLUR_RADIUS),
+            flowWhenShadeIsNotExpanded =
+                transitionAnimation.sharedFlow(
+                    duration = FromPrimaryBouncerTransitionInteractor.TO_LOCKSCREEN_DURATION,
+                    onStep = {
+                        MathUtils.lerp(MAX_BACKGROUND_BLUR_RADIUS, MIN_BACKGROUND_BLUR_RADIUS, it)
+                    },
+                ),
+        )
 }
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java
index 70ca824..dccf61d 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java
@@ -1369,8 +1369,9 @@
         boolean visible = mediaAction != null && !shouldBeHiddenDueToScrubbing;
 
         int notVisibleValue;
-        if ((buttonId == R.id.actionPrev && semanticActions.getReservePrev())
-                || (buttonId == R.id.actionNext && semanticActions.getReserveNext())) {
+        if (!shouldBeHiddenDueToScrubbing
+                && ((buttonId == R.id.actionPrev && semanticActions.getReservePrev())
+                    || (buttonId == R.id.actionNext && semanticActions.getReserveNext()))) {
             notVisibleValue = ConstraintSet.INVISIBLE;
             mMediaViewHolder.getAction(buttonId).setFocusable(visible);
             mMediaViewHolder.getAction(buttonId).setClickable(visible);
@@ -1408,7 +1409,9 @@
         // The scrubbing time views replace the SEMANTIC_ACTIONS_HIDE_WHEN_SCRUBBING action views,
         // so we should only allow scrubbing times to be shown if those action views are present.
         return semanticActions != null && SEMANTIC_ACTIONS_HIDE_WHEN_SCRUBBING.stream().allMatch(
-                id -> semanticActions.getActionById(id) != null
+                id -> (semanticActions.getActionById(id) != null
+                        || ((id == R.id.actionPrev && semanticActions.getReservePrev())
+                            || (id == R.id.actionNext && semanticActions.getReserveNext())))
         );
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt
index 4e97f20..61e4d95 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt
@@ -316,8 +316,11 @@
             isVisibleWhenScrubbing = !shouldHideWhenScrubbing,
             notVisibleValue =
                 if (
-                    (buttonId == R.id.actionPrev && model.semanticActionButtons!!.reservePrev) ||
-                        (buttonId == R.id.actionNext && model.semanticActionButtons!!.reserveNext)
+                    !shouldHideWhenScrubbing &&
+                        ((buttonId == R.id.actionPrev &&
+                            model.semanticActionButtons!!.reservePrev) ||
+                            (buttonId == R.id.actionNext &&
+                                model.semanticActionButtons!!.reserveNext))
                 ) {
                     ConstraintSet.INVISIBLE
                 } else {
@@ -382,7 +385,9 @@
         // so we should only allow scrubbing times to be shown if those action views are present.
         return semanticActions?.let {
             SEMANTIC_ACTIONS_HIDE_WHEN_SCRUBBING.stream().allMatch { id: Int ->
-                semanticActions.getActionById(id) != null
+                semanticActions.getActionById(id) != null ||
+                    (id == R.id.actionPrev && semanticActions.reservePrev ||
+                        id == R.id.actionNext && semanticActions.reserveNext)
             }
         } ?: false
     }
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
index 1204cde..2a23620 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
@@ -74,7 +74,7 @@
     context: Context,
     logger: MediaTttReceiverLogger,
     viewCaptureAwareWindowManager: ViewCaptureAwareWindowManager,
-    @Main mainExecutor: DelayableExecutor,
+    @Main private val mainExecutor: DelayableExecutor,
     accessibilityManager: AccessibilityManager,
     configurationController: ConfigurationController,
     dumpManager: DumpManager,
@@ -285,6 +285,14 @@
         } else {
             rippleController.collapseRipple(rippleView, onAnimationEnd)
             animateViewTranslationAndFade(iconContainerView, translationYBy, 0f)
+            mainExecutor.executeDelayed(
+                {
+                    if (view.isAttachedToWindow) {
+                        onAnimationEnd.run()
+                    }
+                },
+                ICON_TRANSLATION_ANIM_DURATION,
+            )
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/mediarouter/data/repository/MediaRouterRepository.kt b/packages/SystemUI/src/com/android/systemui/mediarouter/data/repository/MediaRouterRepository.kt
index a19c9b3..debb667 100644
--- a/packages/SystemUI/src/com/android/systemui/mediarouter/data/repository/MediaRouterRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediarouter/data/repository/MediaRouterRepository.kt
@@ -16,7 +16,6 @@
 
 package com.android.systemui.mediarouter.data.repository
 
-import android.media.projection.StopReason
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.log.LogBuffer
@@ -41,7 +40,7 @@
     val castDevices: StateFlow<List<CastDevice>>
 
     /** Stops the cast to the given device. */
-    fun stopCasting(device: CastDevice, @StopReason stopReason: Int)
+    fun stopCasting(device: CastDevice)
 }
 
 @SysUISingleton
@@ -68,8 +67,8 @@
             .map { it.filter { device -> device.origin == CastDevice.CastOrigin.MediaRouter } }
             .stateIn(scope, SharingStarted.WhileSubscribed(), emptyList())
 
-    override fun stopCasting(device: CastDevice, @StopReason stopReason: Int) {
-        castController.stopCasting(device, stopReason)
+    override fun stopCasting(device: CastDevice) {
+        castController.stopCasting(device)
     }
 
     companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java
index d2b1d54..8f04896f 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java
@@ -148,7 +148,7 @@
                 navBarHelper, navigationModeController, sysUiFlagsContainer,
                 dumpManager, autoHideControllerStore.forDisplay(mContext.getDisplayId()),
                 lightBarController, pipOptional, backAnimation.orElse(null),
-                taskStackChangeListeners);
+                taskStackChangeListeners, displayTracker);
         mIsLargeScreen = isLargeScreen(mContext);
         mIsPhone = determineIfPhone(mContext, deviceStateManager);
         dumpManager.registerDumpable(this);
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
index 2a3aeae..e9b7534 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
@@ -66,6 +66,7 @@
 import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.settings.DisplayTracker;
 import com.android.systemui.shared.recents.utilities.Utilities;
 import com.android.systemui.shared.statusbar.phone.BarTransitions;
 import com.android.systemui.shared.system.QuickStepContract;
@@ -107,7 +108,7 @@
     private LightBarTransitionsController mLightBarTransitionsController;
     private TaskStackChangeListeners mTaskStackChangeListeners;
     private Optional<Pip> mPipOptional;
-    private int mDisplayId;
+    private int mDefaultDisplayId;
     private int mNavigationIconHints;
     private final NavBarHelper.NavbarTaskbarStateUpdater mNavbarTaskbarStateUpdater =
             new NavBarHelper.NavbarTaskbarStateUpdater() {
@@ -141,7 +142,7 @@
         @Override
         public void onLockTaskModeChanged(int mode) {
             mSysUiState.setFlag(SYSUI_STATE_SCREEN_PINNING, mode == LOCK_TASK_MODE_PINNED)
-                    .commitUpdate(mDisplayId);
+                    .commitUpdate(mDefaultDisplayId);
         }
     };
 
@@ -159,7 +160,10 @@
     private final AutoHideUiElement mAutoHideUiElement = new AutoHideUiElement() {
         @Override
         public void synchronizeState() {
-            checkNavBarModes(mDisplayId);
+            Display[] displays = mDisplayTracker.getAllDisplays();
+            for (Display display : displays) {
+                checkNavBarModes(display.getDisplayId());
+            }
         }
 
         @Override
@@ -177,6 +181,8 @@
 
     private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
     private final StatusBarStateController mStatusBarStateController;
+    private DisplayTracker mDisplayTracker;
+
     @Inject
     public TaskbarDelegate(Context context,
             LightBarTransitionsController.Factory lightBarTransitionsControllerFactory,
@@ -203,7 +209,8 @@
             LightBarController lightBarController,
             Optional<Pip> pipOptional,
             BackAnimation backAnimation,
-            TaskStackChangeListeners taskStackChangeListeners) {
+            TaskStackChangeListeners taskStackChangeListeners,
+            DisplayTracker displayTracker) {
         // TODO: adding this in the ctor results in a dagger dependency cycle :(
         mCommandQueue = commandQueue;
         mOverviewProxyService = overviewProxyService;
@@ -218,6 +225,7 @@
         mLightBarTransitionsController = createLightBarTransitionsController();
         mTaskStackChangeListeners = taskStackChangeListeners;
         mEdgeBackGestureHandler = navBarHelper.getEdgeBackGestureHandler();
+        mDisplayTracker = displayTracker;
     }
 
     @Override
@@ -255,7 +263,7 @@
             if (mInitialized) {
                 return;
             }
-            mDisplayId = displayId;
+            mDefaultDisplayId = displayId;
             parseCurrentSysuiState();
             mCommandQueue.addCallback(this);
             mOverviewProxyService.addCallback(this);
@@ -315,7 +323,7 @@
 
     private void parseCurrentSysuiState() {
         NavBarHelper.CurrentSysuiState state = mNavBarHelper.getCurrentSysuiState();
-        if (state.mWindowStateDisplayId == mDisplayId) {
+        if (state.mWindowStateDisplayId == mDefaultDisplayId) {
             mTaskBarWindowState = state.mWindowState;
         }
     }
@@ -340,7 +348,7 @@
                 .setFlag(SYSUI_STATE_NAV_BAR_HIDDEN, !isWindowVisible())
                 .setFlag(SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY,
                         allowSystemGestureIgnoringBarVisibility())
-                .commitUpdate(mDisplayId);
+                .commitUpdate(mDefaultDisplayId);
     }
 
     boolean isOverviewEnabled() {
@@ -466,7 +474,7 @@
 
     @Override
     public void setWindowState(int displayId, int window, int state) {
-        if (displayId == mDisplayId
+        if (displayId == mDefaultDisplayId
                 && window == StatusBarManager.WINDOW_NAVIGATION_BAR
                 && mTaskBarWindowState != state) {
             mTaskBarWindowState = state;
@@ -498,7 +506,7 @@
             nbModeChanged = updateTransitionMode(
                     transitionMode(mTaskbarTransientShowing, appearance));
         }
-        if (displayId == mDisplayId) {
+        if (displayId == mDefaultDisplayId) {
             mLightBarController.onNavigationBarAppearanceChanged(appearance, nbModeChanged,
                     mTransitionMode, navbarColorManagedByIme);
         }
@@ -510,7 +518,7 @@
 
     @Override
     public void showTransient(int displayId, @InsetsType int types, boolean isGestureOnSystemBar) {
-        if (displayId != mDisplayId) {
+        if (displayId != mDefaultDisplayId) {
             return;
         }
         if ((types & WindowInsets.Type.navigationBars()) == 0) {
@@ -524,7 +532,7 @@
 
     @Override
     public void abortTransient(int displayId, @InsetsType int types) {
-        if (displayId != mDisplayId) {
+        if (displayId != mDefaultDisplayId) {
             return;
         }
         if ((types & WindowInsets.Type.navigationBars()) == 0) {
@@ -654,7 +662,7 @@
 
     @Override
     public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
-        pw.println("TaskbarDelegate (displayId=" + mDisplayId + "):");
+        pw.println("TaskbarDelegate (mDefaultDisplayId=" + mDefaultDisplayId + "):");
         pw.println("  mNavigationIconHints=" + mNavigationIconHints);
         pw.println("  mNavigationMode=" + mNavigationMode);
         pw.println("  mDisabledFlags=" + mDisabledFlags);
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
index b1719107..037a1b2 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -26,6 +26,7 @@
 import static com.android.systemui.navigationbar.gestural.Utilities.isTrackpadScroll;
 import static com.android.systemui.navigationbar.gestural.Utilities.isTrackpadThreeFingerSwipe;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_TOUCHPAD_GESTURES_DISABLED;
+import static com.android.wm.shell.windowdecor.DragResizeWindowGeometry.isEdgeResizePermitted;
 
 import static java.util.stream.Collectors.joining;
 
@@ -965,11 +966,14 @@
         return mDesktopModeExcludeRegion.contains(x, y);
     }
 
-    private boolean isWithinTouchRegion(int x, int y) {
+    private boolean isWithinTouchRegion(MotionEvent ev) {
         // If the point is inside the PiP or Nav bar overlay excluded bounds, then ignore the back
         // gesture
+        int x = (int) ev.getX();
+        int y = (int) ev.getY();
         final boolean isInsidePip = mIsInPip && mPipExcludedBounds.contains(x, y);
-        final boolean isInDesktopExcludeRegion = desktopExcludeRegionContains(x, y);
+        final boolean isInDesktopExcludeRegion = desktopExcludeRegionContains(x, y)
+                && isEdgeResizePermitted(ev);
         if (isInsidePip || isInDesktopExcludeRegion
                 || mNavBarOverlayExcludedBounds.contains(x, y)) {
             return false;
@@ -1098,8 +1102,7 @@
                         && isValidTrackpadBackGesture(true /* isTrackpadEvent */);
             } else {
                 mAllowGesture = isBackAllowedCommon && !mUsingThreeButtonNav && isWithinInsets
-                        && isWithinTouchRegion((int) ev.getX(), (int) ev.getY())
-                        && !isButtonPressFromTrackpad(ev);
+                        && isWithinTouchRegion(ev) && !isButtonPressFromTrackpad(ev);
             }
             if (mAllowGesture) {
                 mEdgeBackPlugin.setIsLeftPanel(mIsOnLeftEdge);
diff --git a/packages/SystemUI/src/com/android/systemui/power/data/repository/PowerRepository.kt b/packages/SystemUI/src/com/android/systemui/power/data/repository/PowerRepository.kt
index 8242087..43bd6aa 100644
--- a/packages/SystemUI/src/com/android/systemui/power/data/repository/PowerRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/power/data/repository/PowerRepository.kt
@@ -27,6 +27,7 @@
 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.power.shared.model.DozeScreenStateModel
 import com.android.systemui.power.shared.model.ScreenPowerState
 import com.android.systemui.power.shared.model.WakeSleepReason
 import com.android.systemui.power.shared.model.WakefulnessModel
@@ -64,6 +65,9 @@
      */
     val screenPowerState: StateFlow<ScreenPowerState>
 
+    /** More granular display states, mainly for use in dozing. */
+    val dozeScreenState: MutableStateFlow<DozeScreenStateModel>
+
     /** Wakes up the device. */
     fun wakeUp(why: String, @PowerManager.WakeReason wakeReason: Int)
 
@@ -100,6 +104,8 @@
     dispatcher: BroadcastDispatcher,
 ) : PowerRepository {
 
+    override val dozeScreenState = MutableStateFlow(DozeScreenStateModel.UNKNOWN)
+
     override val isInteractive: Flow<Boolean> = conflatedCallbackFlow {
         fun send() {
             trySendWithFailureLogging(manager.isInteractive, TAG)
diff --git a/packages/SystemUI/src/com/android/systemui/power/domain/interactor/PowerInteractor.kt b/packages/SystemUI/src/com/android/systemui/power/domain/interactor/PowerInteractor.kt
index 1cf4c23..8a3ee12 100644
--- a/packages/SystemUI/src/com/android/systemui/power/domain/interactor/PowerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/power/domain/interactor/PowerInteractor.kt
@@ -24,6 +24,7 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.power.data.repository.PowerRepository
+import com.android.systemui.power.shared.model.DozeScreenStateModel
 import com.android.systemui.power.shared.model.ScreenPowerState
 import com.android.systemui.power.shared.model.WakeSleepReason
 import com.android.systemui.power.shared.model.WakefulnessModel
@@ -33,6 +34,7 @@
 import javax.inject.Provider
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.map
 
@@ -72,7 +74,11 @@
     /** Helper flow in case "isAsleep" reads better than "!isAwake". */
     val isAsleep = isAwake.map { !it }
 
-    val screenPowerState = repository.screenPowerState
+    /** The physical on/off state of the display. */
+    val screenPowerState: StateFlow<ScreenPowerState> = repository.screenPowerState
+
+    /** The screen state, related to power and controlled by [DozeScreenState] */
+    val dozeScreenState: StateFlow<DozeScreenStateModel> = repository.dozeScreenState.asStateFlow()
 
     /**
      * Notifies the power interactor that a user touch happened.
diff --git a/packages/SystemUI/src/com/android/systemui/power/shared/model/DozeScreenStateModel.kt b/packages/SystemUI/src/com/android/systemui/power/shared/model/DozeScreenStateModel.kt
new file mode 100644
index 0000000..510b90c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/power/shared/model/DozeScreenStateModel.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.power.shared.model
+
+/** Model device doze screen states. */
+enum class DozeScreenStateModel {
+    /** Doze components are set up. Followed by transition to DOZE or DOZE_AOD. */
+    UNKNOWN,
+    /** Regular doze. Device is asleep and listening for pulse triggers. */
+    OFF,
+    /** Deep doze. Device is asleep and is not listening for pulse triggers. */
+    ON,
+    /** Always-on doze. Device is asleep, showing UI and listening for pulse triggers. */
+    DOZE,
+    /** Pulse has been requested. Device is awake and preparing UI */
+    DOZE_SUSPEND,
+    /** Pulse is showing. Device is awake and showing UI. */
+    VR,
+    /** Pulse is showing with bright wallpaper. Device is awake and showing UI. */
+    ON_SUSPEND,
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt b/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt
index 5c9baa0..91a3120 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt
@@ -141,6 +141,7 @@
 class FgsManagerControllerImpl
 @Inject
 constructor(
+    @ShadeDisplayAware private val context: Context,
     @ShadeDisplayAware private val resources: Resources,
     @Main private val mainExecutor: Executor,
     @Background private val backgroundExecutor: Executor,
@@ -387,7 +388,7 @@
     override fun showDialog(expandable: Expandable?) {
         synchronized(lock) {
             if (dialog == null) {
-                val dialog = systemUIDialogFactory.create()
+                val dialog = systemUIDialogFactory.create(context)
                 dialog.setTitle(R.string.fgs_manager_dialog_title)
                 dialog.setMessage(R.string.fgs_manager_dialog_message)
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java
index dc188c2..e8ee4dd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java
@@ -26,6 +26,7 @@
 import android.widget.TextView;
 import android.widget.Toast;
 
+import com.android.systemui.FontStyles;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.qs.dagger.QSScope;
@@ -68,7 +69,7 @@
 
         mBuildText = mView.findViewById(R.id.build);
         if (gsfQuickSettings()) {
-            mBuildText.setTypeface(Typeface.create("gsf-body-medium", Typeface.NORMAL));
+            mBuildText.setTypeface(Typeface.create(FontStyles.GSF_BODY_MEDIUM, Typeface.NORMAL));
         }
         mPageIndicator = mView.findViewById(R.id.footer_page_indicator);
         mEditButton = mView.findViewById(android.R.id.edit);
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 5b8ac64..56fece8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
@@ -48,6 +48,7 @@
 import androidx.compose.foundation.layout.height
 import androidx.compose.foundation.layout.offset
 import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.requiredHeightIn
 import androidx.compose.foundation.verticalScroll
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.runtime.Composable
@@ -76,6 +77,7 @@
 import androidx.compose.ui.semantics.CustomAccessibilityAction
 import androidx.compose.ui.semantics.customActions
 import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.IntOffset
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.round
@@ -627,7 +629,14 @@
                 val Media =
                     @Composable {
                         if (viewModel.qqsMediaVisible) {
-                            MediaObject(mediaHost = viewModel.qqsMediaHost)
+                            MediaObject(
+                                // In order to have stable constraints passed to the AndroidView
+                                // during expansion (available height changing due to squishiness),
+                                // We always allow the media here to be as tall as it wants.
+                                // (b/383085298)
+                                modifier = Modifier.requiredHeightIn(max = Dp.Infinity),
+                                mediaHost = viewModel.qqsMediaHost,
+                            )
                         }
                     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/composefragment/ui/NotificationScrimClip.kt b/packages/SystemUI/src/com/android/systemui/qs/composefragment/ui/NotificationScrimClip.kt
index c912bd5..790793e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/composefragment/ui/NotificationScrimClip.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/ui/NotificationScrimClip.kt
@@ -17,14 +17,16 @@
 package com.android.systemui.qs.composefragment.ui
 
 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.BlendMode
 import androidx.compose.ui.graphics.ClipOp
-import androidx.compose.ui.graphics.Path
-import androidx.compose.ui.graphics.asAndroidPath
-import androidx.compose.ui.graphics.drawscope.ContentDrawScope
-import androidx.compose.ui.graphics.drawscope.clipPath
-import androidx.compose.ui.node.DrawModifierNode
-import androidx.compose.ui.node.ModifierNodeElement
-import androidx.compose.ui.platform.InspectorInfo
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.drawscope.clipRect
+import androidx.compose.ui.graphics.layer.CompositingStrategy
+import androidx.compose.ui.graphics.layer.drawLayer
 
 /**
  * Clipping modifier for clipping out the notification scrim as it slides over QS. It will clip out
@@ -32,65 +34,30 @@
  * from the QS container.
  */
 fun Modifier.notificationScrimClip(clipParams: () -> NotificationScrimClipParams): Modifier {
-    return this then NotificationScrimClipElement(clipParams)
-}
-
-private class NotificationScrimClipNode(var clipParams: () -> NotificationScrimClipParams) :
-    DrawModifierNode, Modifier.Node() {
-    private val path = Path()
-
-    private var lastClipParams = NotificationScrimClipParams()
-
-    override fun ContentDrawScope.draw() {
-        val newClipParams = clipParams()
-        if (newClipParams != lastClipParams) {
-            lastClipParams = newClipParams
-            applyClipParams(path, lastClipParams)
+    return this.drawWithCache {
+            val params = clipParams()
+            val left = -params.leftInset.toFloat()
+            val right = size.width + params.rightInset.toFloat()
+            val top = params.top.toFloat()
+            val bottom = params.bottom.toFloat()
+            val graphicsLayer = obtainGraphicsLayer()
+            graphicsLayer.compositingStrategy = CompositingStrategy.Offscreen
+            graphicsLayer.record {
+                drawContent()
+                clipRect {
+                    drawRoundRect(
+                        color = Color.Black,
+                        cornerRadius = CornerRadius(params.radius.toFloat()),
+                        blendMode = BlendMode.Clear,
+                        topLeft = Offset(left, top),
+                        size = Size(right - left, bottom - top),
+                    )
+                }
+            }
+            onDrawWithContent {
+                drawLayer(graphicsLayer)
+            }
         }
-        clipPath(path, ClipOp.Difference) { this@draw.drawContent() }
-    }
-
-    private fun ContentDrawScope.applyClipParams(
-        path: Path,
-        clipParams: NotificationScrimClipParams,
-    ) {
-        with(clipParams) {
-            path.rewind()
-            path
-                .asAndroidPath()
-                .addRoundRect(
-                    -leftInset.toFloat(),
-                    top.toFloat(),
-                    size.width + rightInset,
-                    bottom.toFloat(),
-                    radius.toFloat(),
-                    radius.toFloat(),
-                    android.graphics.Path.Direction.CW,
-                )
-        }
-    }
-}
-
-private data class NotificationScrimClipElement(val clipParams: () -> NotificationScrimClipParams) :
-    ModifierNodeElement<NotificationScrimClipNode>() {
-    override fun create(): NotificationScrimClipNode {
-        return NotificationScrimClipNode(clipParams)
-    }
-
-    override fun update(node: NotificationScrimClipNode) {
-        node.clipParams = clipParams
-    }
-
-    override fun InspectorInfo.inspectableProperties() {
-        name = "notificationScrimClip"
-        with(clipParams()) {
-            properties["leftInset"] = leftInset
-            properties["top"] = top
-            properties["rightInset"] = rightInset
-            properties["bottom"] = bottom
-            properties["radius"] = radius
-        }
-    }
 }
 
 /** Params for [notificationScrimClip]. */
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
index db778a2..873059e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
@@ -47,6 +47,7 @@
 
 import com.android.internal.logging.UiEventLogger;
 import com.android.systemui.FontSizeUtils;
+import com.android.systemui.FontStyles;
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.flags.Flags;
 import com.android.systemui.qs.QSEditEvent;
@@ -314,7 +315,7 @@
             v.setMinimumHeight(calculateHeaderMinHeight(context));
             if (gsfQuickSettings()) {
                 ((TextView) v.findViewById(android.R.id.title)).setTypeface(
-                        Typeface.create("gsf-label-large", Typeface.NORMAL));
+                        Typeface.create(FontStyles.GSF_LABEL_LARGE, Typeface.NORMAL));
             }
             return new Holder(v);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractor.kt
index 7161565..7b9f42c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractor.kt
@@ -40,6 +40,7 @@
 import com.android.systemui.qs.footer.data.repository.ForegroundServicesRepository
 import com.android.systemui.qs.footer.domain.model.SecurityButtonConfig
 import com.android.systemui.security.data.repository.SecurityRepository
+import com.android.systemui.shade.ShadeDisplayAware
 import com.android.systemui.statusbar.policy.DeviceProvisionedController
 import com.android.systemui.user.data.repository.UserSwitcherRepository
 import com.android.systemui.user.domain.interactor.UserSwitcherInteractor
@@ -108,6 +109,7 @@
     userSwitcherRepository: UserSwitcherRepository,
     broadcastDispatcher: BroadcastDispatcher,
     @Background bgDispatcher: CoroutineDispatcher,
+    @ShadeDisplayAware private val context: Context,
 ) : FooterActionsInteractor {
     override val securityButtonConfig: Flow<SecurityButtonConfig?> =
         securityRepository.security.map { security ->
@@ -157,6 +159,7 @@
             /* keyguardShowing= */ false,
             /* isDeviceProvisioned= */ true,
             expandable,
+            context.displayId,
         )
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
index b7ebce2..d401b6e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
@@ -60,6 +60,7 @@
 import com.android.settingslib.Utils
 import com.android.systemui.Flags
 import com.android.systemui.FontSizeUtils
+import com.android.systemui.FontStyles
 import com.android.systemui.animation.Expandable
 import com.android.systemui.animation.LaunchableView
 import com.android.systemui.animation.LaunchableViewDelegate
@@ -312,9 +313,11 @@
 
         if (Flags.gsfQuickSettings()) {
             label.apply {
-                typeface = Typeface.create("gsf-title-small-emphasized", Typeface.NORMAL)
+                typeface = Typeface.create(FontStyles.GSF_TITLE_SMALL_EMPHASIZED, Typeface.NORMAL)
             }
-            secondaryLabel.apply { typeface = Typeface.create("gsf-label-medium", Typeface.NORMAL) }
+            secondaryLabel.apply {
+                typeface = Typeface.create(FontStyles.GSF_LABEL_MEDIUM, Typeface.NORMAL)
+            }
         }
 
         addView(labelContainer)
@@ -776,11 +779,15 @@
         lastIconTint = icon.getColor(state)
 
         // Long-press effects
-        longPressEffect?.qsTile?.state?.handlesLongClick = state.handlesLongClick
-        if (
-            state.handlesLongClick &&
-                longPressEffect?.initializeEffect(longPressEffectDuration) == true
-        ) {
+        updateLongPressEffect(state.handlesLongClick)
+    }
+
+    private fun updateLongPressEffect(handlesLongClick: Boolean) {
+        // The long press effect in the tile can't be updated if it is still running
+        if (longPressEffect?.state != QSLongPressEffect.State.IDLE) return
+
+        longPressEffect.qsTile?.state?.handlesLongClick = handlesLongClick
+        if (handlesLongClick && longPressEffect.initializeEffect(longPressEffectDuration)) {
             showRippleEffect = false
             longPressEffect.qsTile?.state?.state = lastState // Store the tile's state
             longPressEffect.resetState()
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
index 30c2adf..ad027b4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
@@ -24,7 +24,6 @@
 import android.app.Dialog;
 import android.content.Intent;
 import android.media.MediaRouter.RouteInfo;
-import android.media.projection.StopReason;
 import android.os.Handler;
 import android.os.Looper;
 import android.provider.Settings;
@@ -184,7 +183,7 @@
                 });
             }
         } else {
-            mController.stopCasting(activeDevices.get(0), StopReason.STOP_QS_TILE);
+            mController.stopCasting(activeDevices.get(0));
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
index deeef55..42a0cb1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
@@ -102,7 +102,7 @@
         // Show a dialog to confirm first. Dialogs shown by the DialogTransitionAnimator must be
         // created and shown on the main thread, so we post it to the UI handler.
         mUiHandler.post(() -> {
-            SystemUIDialog dialog = mSystemUIDialogFactory.create();
+            SystemUIDialog dialog = mSystemUIDialogFactory.create(mContext);
             dialog.setTitle(com.android.internal.R.string.data_saver_enable_title);
             dialog.setMessage(com.android.internal.R.string.data_saver_description);
             dialog.setPositiveButton(com.android.internal.R.string.data_saver_enable_button,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
index 4fb96e7..fc82592 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
@@ -18,12 +18,12 @@
 
 import android.app.Dialog;
 import android.content.Intent;
-import android.media.projection.StopReason;
 import android.os.Handler;
 import android.os.Looper;
 import android.service.quicksettings.Tile;
 import android.text.TextUtils;
 import android.util.Log;
+import android.widget.Button;
 import android.widget.Switch;
 
 import androidx.annotation.Nullable;
@@ -144,8 +144,10 @@
         // Show expand icon when clicking will open a dialog
         state.forceExpandIcon = state.state == Tile.STATE_INACTIVE;
 
+        state.expandedAccessibilityClassName = Button.class.getName();
         if (isRecording) {
             state.secondaryLabel = mContext.getString(R.string.quick_settings_screen_record_stop);
+            state.expandedAccessibilityClassName = Switch.class.getName();
         } else if (isStarting) {
             int countdown =
                     (int) ScreenRecordModel.Starting.Companion.toCountdownSeconds(
@@ -157,7 +159,6 @@
         state.contentDescription = TextUtils.isEmpty(state.secondaryLabel)
                 ? state.label
                 : TextUtils.concat(state.label, ", ", state.secondaryLabel);
-        state.expandedAccessibilityClassName = Switch.class.getName();
     }
 
     @Override
@@ -225,7 +226,7 @@
     }
 
     private void stopRecording() {
-        mController.stopRecording(StopReason.STOP_QS_TILE);
+        mController.stopRecording();
     }
 
     private final class Callback implements RecordingController.RecordingStateChangeCallback {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegate.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegate.java
index 378d553..70c2a2a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegate.java
@@ -104,6 +104,7 @@
     private final Handler mHandler;
     private final Executor mBackgroundExecutor;
     private final DialogTransitionAnimator mDialogTransitionAnimator;
+    private final Context mContext;
     private final boolean mAboveStatusBar;
     private final SystemUIDialog.Factory mSystemUIDialogFactory;
 
@@ -204,6 +205,7 @@
             @Background Executor executor,
             KeyguardStateController keyguardStateController,
             SystemUIDialog.Factory systemUIDialogFactory) {
+        mContext = context;
         mAboveStatusBar = aboveStatusBar;
         mSystemUIDialogFactory = systemUIDialogFactory;
         if (DEBUG) {
@@ -228,7 +230,7 @@
 
     @Override
     public SystemUIDialog createDialog() {
-        SystemUIDialog dialog = mSystemUIDialogFactory.create(this);
+        SystemUIDialog dialog = mSystemUIDialogFactory.create(this, mContext);
         if (!mAboveStatusBar) {
             dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverDialogDelegate.kt
index 671943c..d0f2580 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverDialogDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverDialogDelegate.kt
@@ -20,18 +20,19 @@
 import android.content.DialogInterface
 import android.content.SharedPreferences
 import android.os.Bundle
+import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.internal.R
 import com.android.systemui.coroutines.newTracingContext
 import com.android.systemui.qs.tiles.impl.saver.domain.interactor.DataSaverTileUserActionInteractor
+import com.android.systemui.shade.ShadeDisplayAware
 import com.android.systemui.statusbar.phone.SystemUIDialog
 import com.android.systemui.statusbar.policy.DataSaverController
 import kotlin.coroutines.CoroutineContext
 import kotlinx.coroutines.CoroutineScope
-import com.android.app.tracing.coroutines.launchTraced as launch
 
 class DataSaverDialogDelegate(
     private val sysuiDialogFactory: SystemUIDialog.Factory,
-    private val context: Context,
+    @ShadeDisplayAware private val context: Context,
     private val backgroundContext: CoroutineContext,
     private val dataSaverController: DataSaverController,
     private val sharedPreferences: SharedPreferences,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractor.kt
index 9453447..85aa674 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractor.kt
@@ -16,7 +16,6 @@
 
 package com.android.systemui.qs.tiles.impl.screenrecord.domain.interactor
 
-import android.media.projection.StopReason
 import android.util.Log
 import com.android.internal.jank.InteractionJankMonitor
 import com.android.systemui.animation.DialogCuj
@@ -62,9 +61,7 @@
                             Log.d(TAG, "Cancelling countdown")
                             withContext(backgroundContext) { recordingController.cancelCountdown() }
                         }
-                        is ScreenRecordModel.Recording -> {
-                            screenRecordRepository.stopRecording(StopReason.STOP_QS_TILE)
-                        }
+                        is ScreenRecordModel.Recording -> screenRecordRepository.stopRecording()
                         is ScreenRecordModel.DoingNothing ->
                             withContext(mainContext) {
                                 showPrompt(action.expandable, user.identifier)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt b/packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt
index 95e7f56..8c54ab40 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt
@@ -25,7 +25,6 @@
 import android.view.LayoutInflater
 import com.android.internal.jank.InteractionJankMonitor
 import com.android.internal.logging.UiEventLogger
-import com.android.systemui.res.R
 import com.android.systemui.animation.DialogCuj
 import com.android.systemui.animation.DialogTransitionAnimator
 import com.android.systemui.animation.Expandable
@@ -34,22 +33,23 @@
 import com.android.systemui.plugins.FalsingManager
 import com.android.systemui.qs.QSUserSwitcherEvent
 import com.android.systemui.qs.tiles.UserDetailView
+import com.android.systemui.res.R
 import com.android.systemui.statusbar.phone.SystemUIDialog
 import com.android.systemui.user.ui.dialog.DialogShowerImpl
 import javax.inject.Inject
 import javax.inject.Provider
 
-/**
- * Controller for [UserDialog].
- */
+/** Controller for [UserDialog]. */
 @SysUISingleton
-class UserSwitchDialogController @Inject constructor(
+class UserSwitchDialogController
+@Inject
+constructor(
     private val userDetailViewAdapterProvider: Provider<UserDetailView.Adapter>,
     private val activityStarter: ActivityStarter,
     private val falsingManager: FalsingManager,
     private val dialogTransitionAnimator: DialogTransitionAnimator,
     private val uiEventLogger: UiEventLogger,
-    private val dialogFactory: SystemUIDialog.Factory
+    private val dialogFactory: SystemUIDialog.Factory,
 ) {
 
     companion object {
@@ -64,7 +64,7 @@
      * [userDetailViewAdapterProvider] and show it as launched from [expandable].
      */
     fun showDialog(context: Context, expandable: Expandable) {
-        with(dialogFactory.create()) {
+        with(dialogFactory.create(context)) {
             setShowForAllUsers(true)
             setCanceledOnTouchOutside(true)
 
@@ -72,24 +72,31 @@
             setPositiveButton(R.string.quick_settings_done) { _, _ ->
                 uiEventLogger.log(QSUserSwitcherEvent.QS_USER_DETAIL_CLOSE)
             }
-            setNeutralButton(R.string.quick_settings_more_user_settings, { _, _ ->
-                if (!falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
-                    uiEventLogger.log(QSUserSwitcherEvent.QS_USER_MORE_SETTINGS)
-                    val controller = dialogTransitionAnimator.createActivityTransitionController(
-                        getButton(BUTTON_NEUTRAL)
-                    )
+            setNeutralButton(
+                R.string.quick_settings_more_user_settings,
+                { _, _ ->
+                    if (!falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
+                        uiEventLogger.log(QSUserSwitcherEvent.QS_USER_MORE_SETTINGS)
+                        val controller =
+                            dialogTransitionAnimator.createActivityTransitionController(
+                                getButton(BUTTON_NEUTRAL)
+                            )
 
-                    if (controller == null) {
-                        dismiss()
+                        if (controller == null) {
+                            dismiss()
+                        }
+
+                        activityStarter.postStartActivityDismissingKeyguard(
+                            USER_SETTINGS_INTENT,
+                            0,
+                            controller,
+                        )
                     }
-
-                    activityStarter.postStartActivityDismissingKeyguard(
-                        USER_SETTINGS_INTENT, 0, controller
-                    )
-                }
-            }, false /* dismissOnClick */)
-            val gridFrame = LayoutInflater.from(this.context)
-                .inflate(R.layout.qs_user_dialog_content, null)
+                },
+                false, /* dismissOnClick */
+            )
+            val gridFrame =
+                LayoutInflater.from(this.context).inflate(R.layout.qs_user_dialog_content, null)
             setView(gridFrame)
 
             val adapter = userDetailViewAdapterProvider.get()
@@ -101,10 +108,7 @@
                     DialogCuj(InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN, INTERACTION_JANK_TAG)
                 )
             if (controller != null) {
-                dialogTransitionAnimator.show(
-                    this,
-                    controller,
-                )
+                dialogTransitionAnimator.show(this, controller)
             } else {
                 show()
             }
diff --git a/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayInnerDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayInnerDialogDelegate.kt
index 2d6181a..1355ba8 100644
--- a/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayInnerDialogDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayInnerDialogDelegate.kt
@@ -47,7 +47,11 @@
     }
 
     override fun createDialog(): SystemUIDialog {
-        return systemUIDialogFactory.create(this, rearDisplayContext)
+        return systemUIDialogFactory.create(
+            this,
+            rearDisplayContext,
+            false, /* shouldAcsdDismissDialog */
+        )
     }
 
     override fun onCreate(dialog: SystemUIDialog, savedInstanceState: Bundle?) {
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
index d83d74e..0e6fc36 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
@@ -230,13 +230,7 @@
     ) {
         val currentSceneKey = currentScene.value
         val resolvedScene = sceneFamilyResolvers.get()[toScene]?.resolvedScene?.value ?: toScene
-        if (
-            !validateSceneChange(
-                from = currentSceneKey,
-                to = resolvedScene,
-                loggingReason = loggingReason,
-            )
-        ) {
+        if (!validateSceneChange(to = resolvedScene, loggingReason = loggingReason)) {
             return
         }
 
@@ -268,13 +262,7 @@
                     familyResolver.resolvedScene.value
                 }
             } ?: toScene
-        if (
-            !validateSceneChange(
-                from = currentSceneKey,
-                to = resolvedScene,
-                loggingReason = loggingReason,
-            )
-        ) {
+        if (!validateSceneChange(to = resolvedScene, loggingReason = loggingReason)) {
             return
         }
 
@@ -458,12 +446,11 @@
      * Will throw a runtime exception for illegal states (for example, attempting to change to a
      * scene that's not part of the current scene framework configuration).
      *
-     * @param from The current scene being transitioned away from
      * @param to The desired destination scene to transition to
      * @param loggingReason The reason why the transition is requested, for logging purposes
      * @return `true` if the scene change is valid; `false` if it shouldn't happen
      */
-    private fun validateSceneChange(from: SceneKey, to: SceneKey, loggingReason: String): Boolean {
+    private fun validateSceneChange(to: SceneKey, loggingReason: String): Boolean {
         if (to !in repository.allContentKeys) {
             return false
         }
@@ -486,7 +473,7 @@
                 " Logging reason for scene change was: $loggingReason"
         }
 
-        return from != to
+        return true
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/HomeSceneFamilyResolver.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/HomeSceneFamilyResolver.kt
index b89eb5c..2a0a22f 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/HomeSceneFamilyResolver.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/HomeSceneFamilyResolver.kt
@@ -26,6 +26,7 @@
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.scene.shared.model.SceneFamilies
 import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.util.kotlin.combine
 import dagger.Binds
 import dagger.Module
 import dagger.multibindings.IntoSet
@@ -34,7 +35,6 @@
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.stateIn
 
 /**
@@ -59,6 +59,7 @@
                 deviceEntryInteractor.isDeviceEntered,
                 deviceEntryInteractor.isUnlocked,
                 keyguardInteractor.isDreamingWithOverlay,
+                keyguardInteractor.isAbleToDream,
                 transform = ::homeScene,
             )
             .stateIn(
@@ -71,6 +72,7 @@
                         isDeviceEntered = deviceEntryInteractor.isDeviceEntered.value,
                         isUnlocked = deviceEntryInteractor.isUnlocked.value,
                         isDreamingWithOverlay = false,
+                        isAbleToDream = false,
                     ),
             )
 
@@ -82,10 +84,11 @@
         isDeviceEntered: Boolean,
         isUnlocked: Boolean,
         isDreamingWithOverlay: Boolean,
+        isAbleToDream: Boolean,
     ): SceneKey =
         when {
             // Dream can run even if Keyguard is disabled, thus it has the highest priority here.
-            isDreamingWithOverlay -> Scenes.Dream
+            isDreamingWithOverlay && isAbleToDream -> Scenes.Dream
             !isKeyguardEnabled -> Scenes.Gone
             canSwipeToEnter == true -> Scenes.Lockscreen
             !isDeviceEntered -> Scenes.Lockscreen
diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlag.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlag.kt
index 6097ef5..33bffc2 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlag.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlag.kt
@@ -22,7 +22,6 @@
 import com.android.systemui.Flags.sceneContainer
 import com.android.systemui.flags.FlagToken
 import com.android.systemui.flags.RefactorFlagUtils
-import com.android.systemui.keyguard.KeyguardBottomAreaRefactor
 import com.android.systemui.keyguard.KeyguardWmStateRefactor
 import com.android.systemui.keyguard.MigrateClocksToBlueprint
 import com.android.systemui.statusbar.notification.shared.NotificationThrottleHun
@@ -37,7 +36,6 @@
     inline val isEnabled
         get() =
             sceneContainer() && // mainAconfigFlag
-                KeyguardBottomAreaRefactor.isEnabled &&
                 KeyguardWmStateRefactor.isEnabled &&
                 MigrateClocksToBlueprint.isEnabled &&
                 NotificationThrottleHun.isEnabled &&
@@ -51,7 +49,6 @@
     /** The set of secondary flags which must be enabled for scene container to work properly */
     inline fun getSecondaryFlags(): Sequence<FlagToken> =
         sequenceOf(
-            KeyguardBottomAreaRefactor.token,
             KeyguardWmStateRefactor.token,
             MigrateClocksToBlueprint.token,
             NotificationThrottleHun.token,
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
index 9ee99e4..d7463f8 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
@@ -23,7 +23,6 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.media.projection.StopReason;
 import android.os.Bundle;
 import android.os.CountDownTimer;
 import android.os.Process;
@@ -59,7 +58,6 @@
     private boolean mIsStarting;
     private boolean mIsRecording;
     private PendingIntent mStopIntent;
-    private @StopReason int mStopReason = StopReason.STOP_UNKNOWN;
     private final Bundle mInteractiveBroadcastOption;
     private CountDownTimer mCountDownTimer = null;
     private final Executor mMainExecutor;
@@ -85,7 +83,7 @@
             new UserTracker.Callback() {
                 @Override
                 public void onUserChanged(int newUser, @NonNull Context userContext) {
-                    stopRecording(StopReason.STOP_USER_SWITCH);
+                    stopRecording();
                 }
             };
 
@@ -242,11 +240,9 @@
     }
 
     /**
-     * Stop the recording and sets the stop reason to be used by the RecordingService
-     * @param stopReason the method of the recording stopped (i.e. QS tile, status bar chip, etc.)
+     * Stop the recording
      */
-    public void stopRecording(@StopReason int stopReason) {
-        mStopReason = stopReason;
+    public void stopRecording() {
         try {
             if (mStopIntent != null) {
                 mRecordingControllerLogger.logRecordingStopped();
@@ -281,10 +277,6 @@
         }
     }
 
-    public @StopReason int getStopReason() {
-        return mStopReason;
-    }
-
     @Override
     public void addCallback(@NonNull RecordingStateChangeCallback listener) {
         mListeners.add(listener);
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
index f7b5271..8c207d1 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
@@ -26,7 +26,6 @@
 import android.content.Intent;
 import android.graphics.drawable.Icon;
 import android.media.MediaRecorder;
-import android.media.projection.StopReason;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Handler;
@@ -79,7 +78,6 @@
     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";
-    private static final String EXTRA_STOP_REASON = "extra_stopReason";
 
     protected static final String ACTION_START = "com.android.systemui.screenrecord.START";
     protected static final String ACTION_SHOW_START_NOTIF =
@@ -244,8 +242,7 @@
                 // Check user ID - we may be getting a stop intent after user switch, in which case
                 // we want to post the notifications for that user, which is NOT current user
                 int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_ID_NOT_SPECIFIED);
-                int stopReason = intent.getIntExtra(EXTRA_STOP_REASON, mController.getStopReason());
-                stopService(userId, stopReason);
+                stopService(userId);
                 break;
 
             case ACTION_SHARE:
@@ -489,11 +486,11 @@
                 getTag(), notificationIdForGroup, groupNotif, currentUser);
     }
 
-    private void stopService(@StopReason int stopReason) {
-        stopService(USER_ID_NOT_SPECIFIED, stopReason);
+    private void stopService() {
+        stopService(USER_ID_NOT_SPECIFIED);
     }
 
-    private void stopService(int userId, @StopReason int stopReason) {
+    private void stopService(int userId) {
         if (userId == USER_ID_NOT_SPECIFIED) {
             userId = mUserContextTracker.getUserContext().getUserId();
         }
@@ -502,7 +499,7 @@
         setTapsVisible(mOriginalShowTaps);
         try {
             if (getRecorder() != null) {
-                getRecorder().end(stopReason);
+                getRecorder().end();
             }
             saveRecording(userId);
         } catch (RuntimeException exception) {
@@ -601,8 +598,7 @@
      * @return
      */
     protected Intent getNotificationIntent(Context context) {
-        return new Intent(context, this.getClass()).setAction(ACTION_STOP_NOTIF)
-                .putExtra(EXTRA_STOP_REASON, StopReason.STOP_HOST_APP);
+        return new Intent(context, this.getClass()).setAction(ACTION_STOP_NOTIF);
     }
 
     private Intent getShareIntent(Context context, Uri path) {
@@ -614,17 +610,14 @@
     @Override
     public void onInfo(MediaRecorder mr, int what, int extra) {
         Log.d(getTag(), "Media recorder info: " + what);
-        // Stop due to record reaching size limits so log as stopping due to error
-        Intent stopIntent = getStopIntent(this);
-        stopIntent.putExtra(EXTRA_STOP_REASON, StopReason.STOP_ERROR);
-        onStartCommand(stopIntent, 0, 0);
+        onStartCommand(getStopIntent(this), 0, 0);
     }
 
     @Override
-    public void onStopped(@StopReason int stopReason) {
+    public void onStopped() {
         if (mController.isRecording()) {
             Log.d(getTag(), "Stopping recording because the system requested the stop");
-            stopService(stopReason);
+            stopService();
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java
index f4455bf..2ca0621 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java
@@ -41,7 +41,6 @@
 import android.media.projection.IMediaProjectionManager;
 import android.media.projection.MediaProjection;
 import android.media.projection.MediaProjectionManager;
-import android.media.projection.StopReason;
 import android.net.Uri;
 import android.os.Handler;
 import android.os.IBinder;
@@ -301,7 +300,7 @@
     /**
      * End screen recording, throws an exception if stopping recording failed
      */
-    void end(@StopReason int stopReason) throws IOException {
+    void end() throws IOException {
         Closer closer = new Closer();
 
         // MediaRecorder might throw RuntimeException if stopped immediately after starting
@@ -310,17 +309,7 @@
         closer.register(mMediaRecorder::release);
         closer.register(mInputSurface::release);
         closer.register(mVirtualDisplay::release);
-        closer.register(() -> {
-            if (stopReason == StopReason.STOP_UNKNOWN) {
-                // Attempt to call MediaProjection#stop() even if it might have already been called.
-                // If projection has already been stopped, then nothing will happen. Else, stop
-                // will be logged as a manually requested stop from host app.
-                mMediaProjection.stop();
-            } else {
-                // In any other case, the stop reason is related to the recorder, so pass it on here
-                mMediaProjection.stop(stopReason);
-            }
-        });
+        closer.register(mMediaProjection::stop);
         closer.register(this::stopInternalAudioRecording);
 
         closer.close();
@@ -334,7 +323,7 @@
     @Override
     public void onStop() {
         Log.d(TAG, "The system notified about stopping the projection");
-        mListener.onStopped(StopReason.STOP_UNKNOWN);
+        mListener.onStopped();
     }
 
     private void stopInternalAudioRecording() {
@@ -464,7 +453,7 @@
          * For example, this might happen when doing partial screen sharing of an app
          * and the app that is being captured is closed.
          */
-        void onStopped(@StopReason int stopReason);
+        void onStopped();
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/data/repository/ScreenRecordRepository.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/data/repository/ScreenRecordRepository.kt
index b6b8ffa..9eeb3b9 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/data/repository/ScreenRecordRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/data/repository/ScreenRecordRepository.kt
@@ -16,7 +16,6 @@
 
 package com.android.systemui.screenrecord.data.repository
 
-import android.media.projection.StopReason
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.screenrecord.RecordingController
@@ -42,7 +41,7 @@
     val screenRecordState: Flow<ScreenRecordModel>
 
     /** Stops the recording. */
-    suspend fun stopRecording(@StopReason stopReason: Int)
+    suspend fun stopRecording()
 }
 
 @SysUISingleton
@@ -96,7 +95,7 @@
         }
     }
 
-    override suspend fun stopRecording(@StopReason stopReason: Int) {
-        withContext(bgCoroutineContext) { recordingController.stopRecording(stopReason) }
+    override suspend fun stopRecording() {
+        withContext(bgCoroutineContext) { recordingController.stopRecording() }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/scrim/ScrimDrawable.java b/packages/SystemUI/src/com/android/systemui/scrim/ScrimDrawable.java
index 3c03d28..a7b51faa 100644
--- a/packages/SystemUI/src/com/android/systemui/scrim/ScrimDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/scrim/ScrimDrawable.java
@@ -35,7 +35,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.graphics.ColorUtils;
 import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
-import static com.android.systemui.Flags.notificationShadeBlur;
+import com.android.systemui.window.flag.WindowBlurFlag;
 
 /**
  * Drawable used on SysUI scrims.
@@ -214,8 +214,9 @@
     public void draw(@NonNull Canvas canvas) {
         mPaint.setColor(mMainColor);
         mPaint.setAlpha(mAlpha);
-        if (notificationShadeBlur()) {
+        if (WindowBlurFlag.isEnabled()) {
             // TODO(b/370555223): Match the alpha to the visual spec when it is finalized.
+            // TODO (b/381263600), wire this at ScrimController, move it to PrimaryBouncerTransition
             mPaint.setAlpha((int) (0.5f * mAlpha));
         }
         if (mConcaveInfo != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java
index 90d27f4..c65c3b8 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java
@@ -21,7 +21,6 @@
 import static com.android.settingslib.display.BrightnessUtils.convertLinearToGammaFloat;
 
 import android.animation.ValueAnimator;
-import android.annotation.NonNull;
 import android.content.Context;
 import android.database.ContentObserver;
 import android.hardware.display.BrightnessInfo;
@@ -42,6 +41,7 @@
 import android.util.Log;
 import android.util.MathUtils;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -72,19 +72,19 @@
 
 public class BrightnessController implements ToggleSlider.Listener, MirroredBrightnessController {
     private static final String TAG = "CentralSurfaces.BrightnessController";
-    private static final int SLIDER_ANIMATION_DURATION = 3000;
+    protected static final int SLIDER_ANIMATION_DURATION = 3000;
 
-    private static final int MSG_UPDATE_SLIDER = 1;
-    private static final int MSG_ATTACH_LISTENER = 2;
-    private static final int MSG_DETACH_LISTENER = 3;
-    private static final int MSG_VR_MODE_CHANGED = 4;
+    protected static final int MSG_UPDATE_SLIDER = 1;
+    protected static final int MSG_ATTACH_LISTENER = 2;
+    protected static final int MSG_DETACH_LISTENER = 3;
+    protected static final int MSG_VR_MODE_CHANGED = 4;
 
-    private static final Uri BRIGHTNESS_MODE_URI =
+    protected static final Uri BRIGHTNESS_MODE_URI =
             Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_MODE);
 
     private final int mDisplayId;
     private final Context mContext;
-    private final ToggleSlider mControl;
+    protected final ToggleSlider mControl;
     private final DisplayManager mDisplayManager;
     private final UserTracker mUserTracker;
     private final DisplayTracker mDisplayTracker;
@@ -109,10 +109,10 @@
     private boolean mTrackingTouch = false; // Brightness adjusted via touch events.
     private volatile boolean mIsVrModeEnabled;
     private boolean mListening;
-    private boolean mExternalChange;
+    protected boolean mExternalChange;
     private boolean mControlValueInitialized;
-    private float mBrightnessMin = PowerManager.BRIGHTNESS_MIN;
-    private float mBrightnessMax = PowerManager.BRIGHTNESS_MAX;
+    protected float mBrightnessMin = PowerManager.BRIGHTNESS_MIN;
+    protected float mBrightnessMax = PowerManager.BRIGHTNESS_MAX;
     private boolean mIsBrightnessOverriddenByWindow = false;
 
     private ValueAnimator mSliderAnimator;
@@ -253,10 +253,8 @@
             if (info == null) {
                 return;
             }
-            mBrightnessMax = info.brightnessMaximum;
-            mBrightnessMin = info.brightnessMinimum;
-            mIsBrightnessOverriddenByWindow = info.isBrightnessOverrideByWindow;
 
+            updateBrightnessInfo(info);
             // Value is passed as intbits, since this is what the message takes.
             final int valueAsIntBits = Float.floatToIntBits(info.brightness);
             mMainHandler.obtainMessage(MSG_UPDATE_SLIDER, valueAsIntBits,
@@ -264,6 +262,12 @@
         }
     };
 
+    protected void updateBrightnessInfo(BrightnessInfo info) {
+        mBrightnessMax = info.brightnessMaximum;
+        mBrightnessMin = info.brightnessMinimum;
+        mIsBrightnessOverriddenByWindow = info.isBrightnessOverrideByWindow;
+    }
+
     private final IVrStateCallbacks mVrStateCallbacks = new IVrStateCallbacks.Stub() {
         @Override
         public void onVrStateChanged(boolean enabled) {
@@ -301,7 +305,7 @@
         }
     };
 
-    private final Handler mMainHandler;
+    protected final Handler mMainHandler;
 
     private final UserTracker.Callback mUserChangedCallback =
             new UserTracker.Callback() {
@@ -459,7 +463,7 @@
         return !mAutomatic && !mTrackingTouch;
     }
 
-    private void updateSlider(float brightnessValue, boolean inVrMode) {
+    protected void updateSlider(float brightnessValue, boolean inVrMode) {
         final float min = mBrightnessMin;
         final float max = mBrightnessMax;
 
@@ -502,12 +506,17 @@
         mSliderAnimator.start();
     }
 
-
+    /** Factory interface for creating a {@link BrightnessController}. */
+    public interface Factory {
+        @NonNull
+        BrightnessController create(ToggleSlider toggleSlider);
+    }
 
     /** Factory for creating a {@link BrightnessController}. */
     @AssistedFactory
-    public interface Factory {
+    public interface BrightnessControllerFactory extends Factory {
         /** Create a {@link BrightnessController} */
+        @NonNull
         BrightnessController create(ToggleSlider toggleSlider);
     }
 
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 503d0bf..02eca74 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java
@@ -25,6 +25,7 @@
 import android.view.ViewGroup;
 import android.widget.SeekBar;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
 import com.android.internal.logging.UiEventLogger;
@@ -42,6 +43,7 @@
 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;
@@ -89,7 +91,7 @@
         }
     };
 
-    BrightnessSliderController(
+    protected BrightnessSliderController(
             BrightnessSliderView brightnessSliderView,
             FalsingManager falsingManager,
             UiEventLogger uiEventLogger,
@@ -247,16 +249,20 @@
         return mView.isVisibleToUser();
     }
 
+    protected void handleSliderProgressChange(SeekBar seekBar, int progress, boolean fromUser) {
+        if (mListener != null) {
+            mListener.onChanged(mTracking, progress, false);
+            if (fromUser) {
+                mBrightnessSliderHapticPlugin.onProgressChanged(progress, true);
+            }
+        }
+    }
+
     private final SeekBar.OnSeekBarChangeListener mSeekListener =
             new SeekBar.OnSeekBarChangeListener() {
         @Override
         public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
-            if (mListener != null) {
-                mListener.onChanged(mTracking, progress, false);
-                if (fromUser) {
-                    mBrightnessSliderHapticPlugin.onProgressChanged(progress, true);
-                }
-            }
+            handleSliderProgressChange(seekBar, progress, fromUser);
         }
 
         @Override
@@ -289,11 +295,21 @@
         }
     };
 
+    /** Factory interface for creating a {@link BrightnessSliderController}. */
+    public interface Factory {
+        @NonNull
+        BrightnessSliderController create(
+                Context context,
+                @Nullable ViewGroup viewRoot);
+
+        int getLayout();
+    }
+
     /**
      * Creates a {@link BrightnessSliderController} with its associated view.
      */
-    public static class Factory {
 
+    public static class BrightnessSliderControllerFactory implements Factory {
         private final FalsingManager mFalsingManager;
         private final UiEventLogger mUiEventLogger;
         private final VibratorHelper mVibratorHelper;
@@ -303,7 +319,7 @@
         private final BrightnessWarningToast mBrightnessWarningToast;
 
         @Inject
-        public Factory(
+        public BrightnessSliderControllerFactory(
                 FalsingManager falsingManager,
                 UiEventLogger uiEventLogger,
                 VibratorHelper vibratorHelper,
@@ -328,6 +344,8 @@
          * @param viewRoot the {@link ViewGroup} that will contain the hierarchy. The inflated
          *                 hierarchy will not be attached
          */
+        @Override
+        @NonNull
         public BrightnessSliderController create(
                 Context context,
                 @Nullable ViewGroup viewRoot) {
@@ -345,7 +363,7 @@
         }
 
         /** Get the layout to inflate based on what slider to use */
-        private int getLayout() {
+        public int getLayout() {
             return R.layout.quick_settings_brightness_dialog;
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderView.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderView.java
index a39d25a..550ac62 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderView.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderView.java
@@ -43,12 +43,12 @@
 public class BrightnessSliderView extends FrameLayout {
 
     @NonNull
-    private ToggleSeekBar mSlider;
+    protected ToggleSeekBar mSlider;
     private DispatchTouchEventListener mListener;
     private Gefingerpoken mOnInterceptListener;
     @Nullable
-    private Drawable mProgressDrawable;
-    private float mScale = 1f;
+    protected Drawable mProgressDrawable;
+    protected float mScale = 1f;
     private final Rect mSystemGestureExclusionRect = new Rect();
 
     public BrightnessSliderView(Context context) {
@@ -65,6 +65,10 @@
         super.onFinishInflate();
         setLayerType(LAYER_TYPE_HARDWARE, null);
 
+        initBrightnessViewComponents();
+    }
+
+    protected void initBrightnessViewComponents() {
         mSlider = requireViewById(R.id.slider);
         mSlider.setAccessibilityLabel(getContentDescription().toString());
         setBoundaryOffset();
@@ -81,7 +85,7 @@
         }
     }
 
-    private void setBoundaryOffset() {
+    protected void setBoundaryOffset() {
          //  BrightnessSliderView uses hardware layer; if the background of its children exceed its
          //  boundary, it'll be cropped. We need to expand its boundary so that the background of
          //  ToggleSeekBar (i.e. the focus state) can be correctly rendered.
@@ -131,7 +135,7 @@
      * @param admin
      * @see ToggleSeekBar#setEnforcedAdmin
      */
-    void setAdminBlocker(ToggleSeekBar.AdminBlocker blocker) {
+    protected void setAdminBlocker(ToggleSeekBar.AdminBlocker blocker) {
         mSlider.setAdminBlocker(blocker);
     }
 
@@ -211,7 +215,7 @@
         }
     }
 
-    private void applySliderScale() {
+    protected void applySliderScale() {
         if (mProgressDrawable != null) {
             final Rect r = mProgressDrawable.getBounds();
             int height = (int) (mProgressDrawable.getIntrinsicHeight() * mScale);
@@ -229,7 +233,7 @@
      * Interface to attach a listener for {@link View#dispatchTouchEvent}.
      */
     @FunctionalInterface
-    interface DispatchTouchEventListener {
+    public interface DispatchTouchEventListener {
         boolean onDispatchTouchEvent(MotionEvent ev);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/ToggleSeekBar.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/ToggleSeekBar.java
index c241f21..a0985fc6 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/ToggleSeekBar.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/ToggleSeekBar.java
@@ -85,12 +85,12 @@
         }
     }
 
-    void setAdminBlocker(AdminBlocker blocker) {
+    public void setAdminBlocker(AdminBlocker blocker) {
         mAdminBlocker = blocker;
         setEnabled(blocker == null);
     }
 
-    interface AdminBlocker {
+    public interface AdminBlocker {
         boolean block();
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/dagger/BrightnessSliderModule.kt b/packages/SystemUI/src/com/android/systemui/settings/brightness/dagger/BrightnessSliderModule.kt
new file mode 100644
index 0000000..fbe442e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/dagger/BrightnessSliderModule.kt
@@ -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 com.android.systemui.settings.brightness.dagger
+
+import com.android.systemui.settings.brightness.BrightnessController
+import com.android.systemui.settings.brightness.BrightnessSliderController
+import dagger.Binds
+import dagger.Module
+
+@Module
+abstract class BrightnessSliderModule {
+
+    @Binds
+    abstract fun bindBrightnessSliderControllerFactory(
+        factory: BrightnessSliderController.BrightnessSliderControllerFactory
+    ): BrightnessSliderController.Factory
+
+    @Binds
+    abstract fun bindBrightnessControllerFactory(
+        factory: BrightnessController.BrightnessControllerFactory
+    ): BrightnessController.Factory
+}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/DebugDrawable.java b/packages/SystemUI/src/com/android/systemui/shade/DebugDrawable.java
index b24edd9..d78f4d8 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/DebugDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/DebugDrawable.java
@@ -24,7 +24,6 @@
 import android.graphics.PixelFormat;
 import android.graphics.drawable.Drawable;
 
-import com.android.keyguard.LockIconViewController;
 import com.android.systemui.scene.shared.flag.SceneContainerFlag;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
 
@@ -39,7 +38,6 @@
     private final NotificationPanelViewController mNotificationPanelViewController;
     private final NotificationPanelView mView;
     private final NotificationStackScrollLayoutController mNotificationStackScrollLayoutController;
-    private final LockIconViewController mLockIconViewController;
     private final QuickSettingsController mQsController;
     private final Set<Integer> mDebugTextUsedYPositions;
     private final Paint mDebugPaint;
@@ -48,13 +46,11 @@
             NotificationPanelViewController notificationPanelViewController,
             NotificationPanelView notificationPanelView,
             NotificationStackScrollLayoutController notificationStackScrollLayoutController,
-            LockIconViewController lockIconViewController,
             QuickSettingsController quickSettingsController
     ) {
         mNotificationPanelViewController = notificationPanelViewController;
         mView = notificationPanelView;
         mNotificationStackScrollLayoutController = notificationStackScrollLayoutController;
-        mLockIconViewController = lockIconViewController;
         mQsController = quickSettingsController;
         mDebugTextUsedYPositions = new HashSet<>();
         mDebugPaint = new Paint();
@@ -91,8 +87,6 @@
         }
         drawDebugInfo(canvas, mNotificationPanelViewController.getClockPositionResult().clockY,
                 Color.GRAY, "mClockPositionResult.clockY");
-        drawDebugInfo(canvas, (int) mLockIconViewController.getTop(), Color.GRAY,
-                "mLockIconViewController.getTop()");
 
         if (mNotificationPanelViewController.isKeyguardShowing()) {
             // Notifications have the space between those two lines.
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 3a6c250..9e88583 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -25,7 +25,6 @@
 import static com.android.keyguard.KeyguardClockSwitch.SMALL;
 import static com.android.systemui.Flags.msdlFeedback;
 import static com.android.systemui.Flags.predictiveBackAnimateShade;
-import static com.android.systemui.Flags.smartspaceRelocateToBottom;
 import static com.android.systemui.classifier.Classifier.BOUNCER_UNLOCK;
 import static com.android.systemui.classifier.Classifier.GENERIC;
 import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS;
@@ -64,6 +63,8 @@
 import android.graphics.Insets;
 import android.graphics.Rect;
 import android.graphics.Region;
+import android.graphics.RenderEffect;
+import android.graphics.Shader;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Trace;
@@ -105,7 +106,6 @@
 import com.android.keyguard.KeyguardStatusViewController;
 import com.android.keyguard.KeyguardUnfoldTransition;
 import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.keyguard.LockIconViewController;
 import com.android.keyguard.dagger.KeyguardQsUserSwitchComponent;
 import com.android.keyguard.dagger.KeyguardStatusBarViewComponent;
 import com.android.keyguard.dagger.KeyguardStatusViewComponent;
@@ -128,11 +128,9 @@
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.flags.Flags;
 import com.android.systemui.fragments.FragmentService;
-import com.android.systemui.keyguard.KeyguardBottomAreaRefactor;
 import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
 import com.android.systemui.keyguard.KeyguardViewConfigurator;
 import com.android.systemui.keyguard.MigrateClocksToBlueprint;
-import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor;
 import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor;
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
@@ -141,9 +139,9 @@
 import com.android.systemui.keyguard.shared.model.TransitionState;
 import com.android.systemui.keyguard.shared.model.TransitionStep;
 import com.android.systemui.keyguard.ui.binder.KeyguardLongPressViewBinder;
+import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition;
 import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel;
 import com.android.systemui.keyguard.ui.viewmodel.GoneToDreamingTransitionViewModel;
-import com.android.systemui.keyguard.ui.viewmodel.KeyguardBottomAreaViewModel;
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardTouchHandlingViewModel;
 import com.android.systemui.keyguard.ui.viewmodel.LockscreenToDreamingTransitionViewModel;
 import com.android.systemui.keyguard.ui.viewmodel.LockscreenToOccludedTransitionViewModel;
@@ -187,7 +185,6 @@
 import com.android.systemui.statusbar.notification.AnimatableProperty;
 import com.android.systemui.statusbar.notification.ConversationNotificationManager;
 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
-import com.android.systemui.statusbar.notification.headsup.HeadsUpTouchHelper;
 import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
 import com.android.systemui.statusbar.notification.PropertyAnimator;
 import com.android.systemui.statusbar.notification.ViewGroupFadeHelper;
@@ -195,6 +192,7 @@
 import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor;
 import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor;
 import com.android.systemui.statusbar.notification.headsup.HeadsUpManager;
+import com.android.systemui.statusbar.notification.headsup.HeadsUpTouchHelper;
 import com.android.systemui.statusbar.notification.headsup.OnHeadsUpChangedListener;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.ExpandableView;
@@ -211,8 +209,6 @@
 import com.android.systemui.statusbar.phone.CentralSurfaces;
 import com.android.systemui.statusbar.phone.DozeParameters;
 import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
-import com.android.systemui.statusbar.phone.KeyguardBottomAreaView;
-import com.android.systemui.statusbar.phone.KeyguardBottomAreaViewController;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.phone.KeyguardClockPositionAlgorithm;
 import com.android.systemui.statusbar.phone.KeyguardStatusBarViewController;
@@ -366,7 +362,7 @@
     private final TouchHandler mTouchHandler = new TouchHandler();
 
     private long mDownTime;
-    private long mStatusBarLongPressDowntime;
+    private long mStatusBarLongPressDowntime = -1L;
     private boolean mTouchSlopExceededBeforeDown;
     private float mOverExpansion;
     private CentralSurfaces mCentralSurfaces;
@@ -374,8 +370,6 @@
     private float mExpandedHeight = 0;
     /** The current squish amount for the predictive back animation */
     private float mCurrentBackProgress = 0.0f;
-    @Deprecated
-    private KeyguardBottomAreaView mKeyguardBottomArea;
     private boolean mExpanding;
     private boolean mSplitShadeEnabled;
     /** The bottom padding reserved for elements of the keyguard measuring notifications. */
@@ -391,11 +385,8 @@
     private KeyguardUserSwitcherController mKeyguardUserSwitcherController;
     private KeyguardStatusBarViewController mKeyguardStatusBarViewController;
     private KeyguardStatusViewController mKeyguardStatusViewController;
-    private final LockIconViewController mLockIconViewController;
     private NotificationsQuickSettingsContainer mNotificationContainerParent;
     private final NotificationsQSContainerController mNotificationsQSContainerController;
-    private final Provider<KeyguardBottomAreaViewController>
-            mKeyguardBottomAreaViewControllerProvider;
     private boolean mAnimateNextPositionUpdate;
     private final ScreenOffAnimationController mScreenOffAnimationController;
     private final UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
@@ -547,8 +538,6 @@
     private final NotificationListContainer mNotificationListContainer;
     private final NotificationStackSizeCalculator mNotificationStackSizeCalculator;
     private final NPVCDownEventState.Buffer mLastDownEvents;
-    private final KeyguardBottomAreaViewModel mKeyguardBottomAreaViewModel;
-    private final KeyguardBottomAreaInteractor mKeyguardBottomAreaInteractor;
     private final KeyguardClockInteractor mKeyguardClockInteractor;
     private float mMinExpandHeight;
     private boolean mPanelUpdateWhenAnimatorEnds;
@@ -624,8 +613,6 @@
     private final SplitShadeStateController mSplitShadeStateController;
     private final Runnable mFlingCollapseRunnable = () -> fling(0, false /* expand */,
             mNextCollapseSpeedUpFactor, false /* expandBecauseOfFalsing */);
-    private final Runnable mAnimateKeyguardBottomAreaInvisibleEndRunnable =
-            () -> mKeyguardBottomArea.setVisibility(View.GONE);
     private final Runnable mHeadsUpExistenceChangedRunnable = () -> {
         setHeadsUpAnimatingAway(false);
         updateExpansionAndVisibility();
@@ -714,7 +701,6 @@
             MediaDataManager mediaDataManager,
             NotificationShadeDepthController notificationShadeDepthController,
             AmbientState ambientState,
-            LockIconViewController lockIconViewController,
             KeyguardMediaController keyguardMediaController,
             TapAgainViewController tapAgainViewController,
             NavigationModeController navigationModeController,
@@ -730,15 +716,12 @@
             ShadeRepository shadeRepository,
             Optional<SysUIUnfoldComponent> unfoldComponent,
             SysUiState sysUiState,
-            Provider<KeyguardBottomAreaViewController> keyguardBottomAreaViewControllerProvider,
             KeyguardUnlockAnimationController keyguardUnlockAnimationController,
             KeyguardIndicationController keyguardIndicationController,
             NotificationListContainer notificationListContainer,
             NotificationStackSizeCalculator notificationStackSizeCalculator,
             UnlockedScreenOffAnimationController unlockedScreenOffAnimationController,
             SystemClock systemClock,
-            KeyguardBottomAreaViewModel keyguardBottomAreaViewModel,
-            KeyguardBottomAreaInteractor keyguardBottomAreaInteractor,
             KeyguardClockInteractor keyguardClockInteractor,
             AlternateBouncerInteractor alternateBouncerInteractor,
             DreamingToLockscreenTransitionViewModel dreamingToLockscreenTransitionViewModel,
@@ -852,7 +835,6 @@
         mNotificationListContainer = notificationListContainer;
         mNotificationStackSizeCalculator = notificationStackSizeCalculator;
         mNavigationBarController = navigationBarController;
-        mKeyguardBottomAreaViewControllerProvider = keyguardBottomAreaViewControllerProvider;
         mNotificationsQSContainerController.init();
         mNotificationStackScrollLayoutController = notificationStackScrollLayoutController;
         mKeyguardStatusViewComponentFactory = keyguardStatusViewComponentFactory;
@@ -908,7 +890,6 @@
         mBottomAreaShadeAlphaAnimator.setInterpolator(Interpolators.ALPHA_OUT);
         mConversationNotificationManager = conversationNotificationManager;
         mAuthController = authController;
-        mLockIconViewController = lockIconViewController;
         mScreenOffAnimationController = screenOffAnimationController;
         mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController;
         mLastDownEvents = new NPVCDownEventState.Buffer(MAX_DOWN_EVENT_BUFFER_SIZE);
@@ -930,16 +911,13 @@
 
         if (DEBUG_DRAWABLE) {
             mView.getOverlay().add(new DebugDrawable(this, mView,
-                    mNotificationStackScrollLayoutController, mLockIconViewController,
-                    mQsController));
+                    mNotificationStackScrollLayoutController, mQsController));
         }
 
         mKeyguardUnfoldTransition = unfoldComponent.map(
                 SysUIUnfoldComponent::getKeyguardUnfoldTransition);
 
         updateUserSwitcherFlags();
-        mKeyguardBottomAreaViewModel = keyguardBottomAreaViewModel;
-        mKeyguardBottomAreaInteractor = keyguardBottomAreaInteractor;
         mKeyguardClockInteractor = keyguardClockInteractor;
         KeyguardLongPressViewBinder.bind(
                 mView.requireViewById(R.id.keyguard_long_press),
@@ -1062,12 +1040,6 @@
         mQsController.init();
         mShadeHeadsUpTracker.addTrackingHeadsUpListener(
                 mNotificationStackScrollLayoutController::setTrackingHeadsUp);
-        if (!KeyguardBottomAreaRefactor.isEnabled()) {
-            setKeyguardBottomArea(mView.findViewById(R.id.keyguard_bottom_area));
-        }
-
-        initBottomArea();
-
         mWakeUpCoordinator.setStackScroller(mNotificationStackScrollLayoutController);
         mWakeUpCoordinator.addListener(new NotificationWakeUpCoordinator.WakeUpListener() {
             @Override
@@ -1183,6 +1155,11 @@
                 }, mMainDispatcher);
         }
 
+        if (com.android.systemui.Flags.bouncerUiRevamp()) {
+            collectFlow(mView, mKeyguardInteractor.primaryBouncerShowing,
+                    this::handleBouncerShowingChanged);
+        }
+
         // Ensures that flags are updated when an activity launches
         collectFlow(mView,
                 mShadeAnimationInteractor.isLaunchingActivity(),
@@ -1232,6 +1209,22 @@
         mQsController.loadDimens();
     }
 
+    private void handleBouncerShowingChanged(Boolean isBouncerShowing) {
+        if (!com.android.systemui.Flags.bouncerUiRevamp()) return;
+
+        if (isBouncerShowing && isExpanded()) {
+            // Blur the shade much lesser than the background surface so that the surface is
+            // distinguishable from the background.
+            float shadeBlurEffect = PrimaryBouncerTransition.MAX_BACKGROUND_BLUR_RADIUS / 3;
+            mView.setRenderEffect(RenderEffect.createBlurEffect(
+                    shadeBlurEffect,
+                    shadeBlurEffect,
+                    Shader.TileMode.MIRROR));
+        } else {
+            mView.setRenderEffect(null);
+        }
+    }
+
     private void updateViewControllers(
             FrameLayout userAvatarView,
             KeyguardUserSwitcherView keyguardUserSwitcherView) {
@@ -1421,23 +1414,6 @@
                         showKeyguardUserSwitcher /* enabled */);
 
         updateViewControllers(userAvatarView, keyguardUserSwitcherView);
-
-        if (!KeyguardBottomAreaRefactor.isEnabled()) {
-            // Update keyguard bottom area
-            int index = mView.indexOfChild(mKeyguardBottomArea);
-            mView.removeView(mKeyguardBottomArea);
-            KeyguardBottomAreaView oldBottomArea = mKeyguardBottomArea;
-            KeyguardBottomAreaViewController keyguardBottomAreaViewController =
-                    mKeyguardBottomAreaViewControllerProvider.get();
-            if (smartspaceRelocateToBottom()) {
-                keyguardBottomAreaViewController.init();
-            }
-            setKeyguardBottomArea(keyguardBottomAreaViewController.getView());
-            mKeyguardBottomArea.initFrom(oldBottomArea);
-            mView.addView(mKeyguardBottomArea, index);
-
-            initBottomArea();
-        }
         mStatusBarStateListener.onDozeAmountChanged(mStatusBarStateController.getDozeAmount(),
                 mStatusBarStateController.getInterpolatedDozeAmount());
 
@@ -1462,10 +1438,6 @@
                     false,
                     mBarState);
         }
-
-        if (!KeyguardBottomAreaRefactor.isEnabled()) {
-            setKeyguardBottomAreaVisibility(mBarState, false);
-        }
     }
 
     private void attachSplitShadeMediaPlayerContainer(FrameLayout container) {
@@ -1475,22 +1447,6 @@
         mKeyguardMediaController.attachSplitShadeContainer(container);
     }
 
-    private void initBottomArea() {
-        if (!KeyguardBottomAreaRefactor.isEnabled()) {
-            mKeyguardBottomArea.init(
-                mKeyguardBottomAreaViewModel,
-                mFalsingManager,
-                mLockIconViewController,
-                stringResourceId ->
-                        mKeyguardIndicationController.showTransientIndication(stringResourceId),
-                mVibratorHelper,
-                mActivityStarter);
-
-            // Rebind (for now), as a new bottom area and indication area may have been created
-            mKeyguardViewConfigurator.bindIndicationArea();
-        }
-    }
-
     @VisibleForTesting
     void setMaxDisplayedNotifications(int maxAllowed) {
         mMaxAllowedKeyguardNotifications = maxAllowed;
@@ -1528,11 +1484,6 @@
         return mUnlockedScreenOffAnimationController.isAnimationPlaying();
     }
 
-    @Deprecated
-    private void setKeyguardBottomArea(KeyguardBottomAreaView keyguardBottomArea) {
-        mKeyguardBottomArea = keyguardBottomArea;
-    }
-
     /** Sets a listener to be notified when the shade starts opening or finishes closing. */
     public void setOpenCloseListener(OpenCloseListener openCloseListener) {
         SceneContainerFlag.assertInLegacyMode();
@@ -1660,10 +1611,6 @@
             mKeyguardStatusViewController.setLockscreenClockY(
                     mClockPositionAlgorithm.getExpandedPreferredClockY());
         }
-        if (!(MigrateClocksToBlueprint.isEnabled() || KeyguardBottomAreaRefactor.isEnabled())) {
-            mKeyguardBottomAreaInteractor.setClockPosition(
-                mClockPositionResult.clockX, mClockPositionResult.clockY);
-        }
 
         boolean animate = !SceneContainerFlag.isEnabled()
                 && mNotificationStackScrollLayoutController.isAddOrRemoveAnimationPending();
@@ -2382,25 +2329,6 @@
         }
     }
 
-    @Deprecated
-    private void setKeyguardBottomAreaVisibility(int statusBarState, boolean goingToFullShade) {
-        mKeyguardBottomArea.animate().cancel();
-        if (goingToFullShade) {
-            mKeyguardBottomArea.animate().alpha(0f).setStartDelay(
-                    mKeyguardStateController.getKeyguardFadingAwayDelay()).setDuration(
-                    mKeyguardStateController.getShortenedFadingAwayDuration()).setInterpolator(
-                    Interpolators.ALPHA_OUT).withEndAction(
-                    mAnimateKeyguardBottomAreaInvisibleEndRunnable).start();
-        } else if (statusBarState == KEYGUARD || statusBarState == StatusBarState.SHADE_LOCKED) {
-            mKeyguardBottomArea.setVisibility(View.VISIBLE);
-            if (!mIsOcclusionTransitionRunning) {
-                mKeyguardBottomArea.setAlpha(1f);
-            }
-        } else {
-            mKeyguardBottomArea.setVisibility(View.GONE);
-        }
-    }
-
     /**
      * When the back gesture triggers a fully-expanded shade --> QQS shade collapse transition,
      * the expansionFraction goes down from 1.0 --> 0.0 (collapsing), so the current "squish" amount
@@ -2755,12 +2683,7 @@
 
         float alpha = Math.min(expansionAlpha, 1 - mQsController.computeExpansionFraction());
         alpha *= mBottomAreaShadeAlpha;
-        if (KeyguardBottomAreaRefactor.isEnabled()) {
-            mKeyguardInteractor.setAlpha(alpha);
-        } else {
-            mKeyguardBottomAreaInteractor.setAlpha(alpha);
-        }
-        mLockIconViewController.setAlpha(alpha);
+        mKeyguardInteractor.setAlpha(alpha);
     }
 
     private void onExpandingFinished() {
@@ -2967,11 +2890,7 @@
     }
 
     private void updateDozingVisibilities(boolean animate) {
-        if (KeyguardBottomAreaRefactor.isEnabled()) {
-            mKeyguardInteractor.setAnimateDozingTransitions(animate);
-        } else {
-            mKeyguardBottomAreaInteractor.setAnimateDozingTransitions(animate);
-        }
+        mKeyguardInteractor.setAnimateDozingTransitions(animate);
         if (!mDozing && animate) {
             mKeyguardStatusBarViewController.animateKeyguardStatusBarIn();
         }
@@ -3185,7 +3104,7 @@
             return false;
         }
         if (mHeadsUpAppearanceController != null
-                && mHeadsUpAppearanceController.shouldBeVisible()) {
+                && mHeadsUpAppearanceController.shouldHeadsUpStatusBarBeVisible()) {
             return false;
         }
         return !mShowIconsWhenExpanded;
@@ -3212,11 +3131,7 @@
         mDozing = dozing;
         // TODO (b/) make listeners for this
         mNotificationStackScrollLayoutController.setDozing(mDozing, animate);
-        if (KeyguardBottomAreaRefactor.isEnabled()) {
-            mKeyguardInteractor.setAnimateDozingTransitions(animate);
-        } else {
-            mKeyguardBottomAreaInteractor.setAnimateDozingTransitions(animate);
-        }
+        mKeyguardInteractor.setAnimateDozingTransitions(animate);
         mKeyguardStatusBarViewController.setDozing(mDozing);
         mQsController.setDozing(mDozing);
 
@@ -3267,7 +3182,6 @@
     }
 
     public void dozeTimeTick() {
-        mLockIconViewController.dozeTimeTick();
         if (!MigrateClocksToBlueprint.isEnabled()) {
             mKeyguardStatusViewController.dozeTimeTick();
         }
@@ -3795,7 +3709,7 @@
     private void endMotionEvent(MotionEvent event, float x, float y, boolean forceCancel) {
         mShadeLog.logEndMotionEvent("endMotionEvent called", forceCancel, false);
         mTrackingPointer = -1;
-        mStatusBarLongPressDowntime = 0L;
+        mStatusBarLongPressDowntime = -1L;
         mAmbientState.setSwipingUp(false);
         if ((isTracking() && mTouchSlopExceeded) || Math.abs(x - mInitialExpandX) > mTouchSlop
                 || Math.abs(y - mInitialExpandY) > mTouchSlop
@@ -4544,10 +4458,6 @@
                         mBarState);
             }
 
-            if (!KeyguardBottomAreaRefactor.isEnabled()) {
-                setKeyguardBottomAreaVisibility(statusBarState, goingToFullShade);
-            }
-
             // TODO: maybe add a listener for barstate
             mBarState = statusBarState;
             mQsController.setBarState(statusBarState);
@@ -4634,7 +4544,7 @@
                 @Override
                 public boolean shouldHeadsUpBeVisible() {
                     return mHeadsUpAppearanceController != null &&
-                            mHeadsUpAppearanceController.shouldBeVisible();
+                            mHeadsUpAppearanceController.shouldHeadsUpStatusBarBeVisible();
                 }
 
                 @Override
@@ -4813,12 +4723,7 @@
                 stackScroller.setMaxAlphaForKeyguard(alpha, "NPVC.setTransitionAlpha()");
             }
 
-            if (KeyguardBottomAreaRefactor.isEnabled()) {
-                mKeyguardInteractor.setAlpha(alpha);
-            } else {
-                mKeyguardBottomAreaInteractor.setAlpha(alpha);
-            }
-            mLockIconViewController.setAlpha(alpha);
+            mKeyguardInteractor.setAlpha(alpha);
 
             if (mKeyguardQsUserSwitchController != null) {
                 mKeyguardQsUserSwitchController.setAlpha(alpha);
@@ -5199,7 +5104,7 @@
                     mUpdateFlingOnLayout = false;
                     mMotionAborted = false;
                     mDownTime = mSystemClock.uptimeMillis();
-                    mStatusBarLongPressDowntime = 0L;
+                    mStatusBarLongPressDowntime = -1L;
                     mTouchAboveFalsingThreshold = false;
                     mCollapsedAndHeadsUpOnDown =
                             isFullyCollapsed() && mHeadsUpManager.hasPinnedHeadsUp();
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowView.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowView.java
index bf672be..48bbb04 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowView.java
@@ -169,6 +169,10 @@
     public void onMovedToDisplay(int displayId, Configuration config) {
         super.onMovedToDisplay(displayId, config);
         ShadeWindowGoesAround.isUnexpectedlyInLegacyMode();
+        ShadeTraceLogger.logOnMovedToDisplay(displayId, config);
+        if (mConfigurationForwarder != null) {
+            mConfigurationForwarder.dispatchOnMovedToDisplay(displayId, config);
+        }
         // When the window is moved we're only receiving a call to this method instead of the
         // onConfigurationChange itself. Let's just trigegr a normal config change.
         onConfigurationChanged(config);
@@ -177,6 +181,7 @@
     @Override
     protected void onConfigurationChanged(Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
+        ShadeTraceLogger.logOnConfigChanged(newConfig);
         if (mConfigurationForwarder != null) {
             ShadeWindowGoesAround.isUnexpectedlyInLegacyMode();
             mConfigurationForwarder.onConfigurationChanged(newConfig);
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadePrimaryDisplayCommand.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadePrimaryDisplayCommand.kt
index a54f6b9..7bfe40c3 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadePrimaryDisplayCommand.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadePrimaryDisplayCommand.kt
@@ -16,23 +16,23 @@
 
 package com.android.systemui.shade
 
-import android.view.Display
+import android.provider.Settings.Global.DEVELOPMENT_SHADE_DISPLAY_AWARENESS
 import com.android.systemui.CoreStartable
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.display.data.repository.DisplayRepository
 import com.android.systemui.shade.data.repository.MutableShadeDisplaysRepository
 import com.android.systemui.shade.display.ShadeDisplayPolicy
-import com.android.systemui.shade.display.SpecificDisplayIdPolicy
 import com.android.systemui.statusbar.commandline.Command
 import com.android.systemui.statusbar.commandline.CommandRegistry
+import com.android.systemui.util.settings.GlobalSettings
 import java.io.PrintWriter
 import javax.inject.Inject
-import kotlin.text.toIntOrNull
 
 @SysUISingleton
 class ShadePrimaryDisplayCommand
 @Inject
 constructor(
+    private val globalSettings: GlobalSettings,
     private val commandRegistry: CommandRegistry,
     private val displaysRepository: DisplayRepository,
     private val positionRepository: MutableShadeDisplaysRepository,
@@ -45,7 +45,7 @@
     }
 
     override fun help(pw: PrintWriter) {
-        pw.println("shade_display_override (<displayId>|<policyName>) ")
+        pw.println("shade_display_override <policyName> ")
         pw.println("Set the display which is holding the shade, or the policy that defines it.")
         pw.println()
         pw.println("shade_display_override policies")
@@ -56,9 +56,6 @@
         pw.println()
         pw.println("shade_display_override (list|status) ")
         pw.println("Lists available displays and which has the shade")
-        pw.println()
-        pw.println("shade_display_override any_external")
-        pw.println("Moves the shade to the first not-default display available")
     }
 
     override fun execute(pw: PrintWriter, args: List<String>) {
@@ -74,28 +71,24 @@
         fun execute() {
             when (val command = args.getOrNull(0)?.lowercase()) {
                 "reset" -> reset()
+                "policies" -> printPolicies()
                 "list",
                 "status" -> printStatus()
-                "policies" -> printPolicies()
-                "any_external" -> anyExternal()
                 null -> help(pw)
                 else -> parsePolicy(command)
             }
         }
 
         private fun parsePolicy(policyIdentifier: String) {
-            val displayId = policyIdentifier.toIntOrNull()
-            when {
-                displayId != null -> changeDisplay(displayId = displayId)
-                policies.any { it.name == policyIdentifier } -> {
-                    positionRepository.policy.value = policies.first { it.name == policyIdentifier }
-                }
-                else -> help(pw)
+            if (policies.any { it.name == policyIdentifier }) {
+                globalSettings.putString(DEVELOPMENT_SHADE_DISPLAY_AWARENESS, policyIdentifier)
+            } else {
+                help(pw)
             }
         }
 
         private fun reset() {
-            positionRepository.policy.value = defaultPolicy
+            globalSettings.putString(DEVELOPMENT_SHADE_DISPLAY_AWARENESS, defaultPolicy.name)
             pw.println("Reset shade display policy to default policy: ${defaultPolicy.name}")
         }
 
@@ -117,30 +110,5 @@
                 pw.println(if (currentPolicyName == it.name) " (Current policy)" else "")
             }
         }
-
-        private fun anyExternal() {
-            val anyExternalDisplay =
-                displaysRepository.displays.value.firstOrNull {
-                    it.displayId != Display.DEFAULT_DISPLAY
-                }
-            if (anyExternalDisplay == null) {
-                pw.println("No external displays available.")
-                return
-            }
-            setDisplay(anyExternalDisplay.displayId)
-        }
-
-        private fun changeDisplay(displayId: Int) {
-            if (displayId < 0) {
-                pw.println("Error: display id should be positive integer")
-            }
-
-            setDisplay(displayId)
-        }
-
-        private fun setDisplay(id: Int) {
-            positionRepository.policy.value = SpecificDisplayIdPolicy(id)
-            pw.println("New shade primary display id is $id")
-        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeTraceLogger.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeTraceLogger.kt
new file mode 100644
index 0000000..4516133
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeTraceLogger.kt
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shade
+
+import android.content.res.Configuration
+import android.os.Trace
+import com.android.app.tracing.TraceUtils.traceAsync
+
+/**
+ * Centralized logging for shade-related events to a dedicated Perfetto track.
+ *
+ * Used by shade components to log events to a track named [TAG]. This consolidates shade-specific
+ * events into a single track for easier analysis in Perfetto, rather than scattering them across
+ * various threads' logs.
+ */
+object ShadeTraceLogger {
+    private const val TAG = "ShadeTraceLogger"
+
+    @JvmStatic
+    fun logOnMovedToDisplay(displayId: Int, config: Configuration) {
+        if (!Trace.isEnabled()) return
+        Trace.instantForTrack(
+            Trace.TRACE_TAG_APP,
+            TAG,
+            "onMovedToDisplay(displayId=$displayId, dpi=" + config.densityDpi + ")",
+        )
+    }
+
+    @JvmStatic
+    fun logOnConfigChanged(config: Configuration) {
+        if (!Trace.isEnabled()) return
+        Trace.instantForTrack(
+            Trace.TRACE_TAG_APP,
+            TAG,
+            "onConfigurationChanged(dpi=" + config.densityDpi + ")",
+        )
+    }
+
+    fun logMoveShadeWindowTo(displayId: Int) {
+        if (!Trace.isEnabled()) return
+        Trace.instantForTrack(Trace.TRACE_TAG_APP, TAG, "moveShadeWindowTo(displayId=$displayId)")
+    }
+
+    fun traceReparenting(r: () -> Unit) {
+        traceAsync(TAG, { "reparenting" }) { r() }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt
index 8937ce3..e191120 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt
@@ -50,7 +50,6 @@
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
 import com.android.systemui.statusbar.notification.stack.ui.view.NotificationScrollView
 import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer
-import com.android.systemui.statusbar.phone.KeyguardBottomAreaView
 import com.android.systemui.statusbar.phone.StatusBarLocation
 import com.android.systemui.statusbar.phone.StatusIconContainer
 import com.android.systemui.statusbar.phone.TapAgainView
@@ -145,20 +144,6 @@
             return notificationShadeWindowView.requireViewById(R.id.notification_panel)
         }
 
-        /**
-         * Constructs a new, unattached [KeyguardBottomAreaView].
-         *
-         * Note that this is explicitly _not_ a singleton, as we want to be able to reinflate it
-         */
-        @Provides
-        fun providesKeyguardBottomAreaView(
-            npv: NotificationPanelView,
-            @ShadeDisplayAware layoutInflater: LayoutInflater,
-        ): KeyguardBottomAreaView {
-            return layoutInflater.inflate(R.layout.keyguard_bottom_area, npv, false)
-                as KeyguardBottomAreaView
-        }
-
         @Provides
         @SysUISingleton
         fun providesLightRevealScrim(
diff --git a/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepository.kt b/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepository.kt
index 756241e..af48231 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepository.kt
@@ -16,17 +16,22 @@
 
 package com.android.systemui.shade.data.repository
 
+import android.provider.Settings.Global.DEVELOPMENT_SHADE_DISPLAY_AWARENESS
 import android.view.Display
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.shade.display.ShadeDisplayPolicy
+import com.android.systemui.util.settings.GlobalSettings
+import com.android.systemui.util.settings.SettingsProxyExt.observerFlow
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onStart
 import kotlinx.coroutines.flow.stateIn
 
 /** Source of truth for the display currently holding the shade. */
@@ -38,7 +43,7 @@
 /** Allows to change the policy that determines in which display the Shade window is visible. */
 interface MutableShadeDisplaysRepository : ShadeDisplaysRepository {
     /** Updates the policy to select where the shade is visible. */
-    val policy: MutableStateFlow<ShadeDisplayPolicy>
+    val policy: StateFlow<ShadeDisplayPolicy>
 }
 
 /** Keeps the policy and propagates the display id for the shade from it. */
@@ -46,9 +51,27 @@
 @OptIn(ExperimentalCoroutinesApi::class)
 class ShadeDisplaysRepositoryImpl
 @Inject
-constructor(defaultPolicy: ShadeDisplayPolicy, @Background bgScope: CoroutineScope) :
-    MutableShadeDisplaysRepository {
-    override val policy = MutableStateFlow<ShadeDisplayPolicy>(defaultPolicy)
+constructor(
+    globalSettings: GlobalSettings,
+    defaultPolicy: ShadeDisplayPolicy,
+    @Background bgScope: CoroutineScope,
+    policies: Set<@JvmSuppressWildcards ShadeDisplayPolicy>,
+) : MutableShadeDisplaysRepository {
+
+    override val policy: StateFlow<ShadeDisplayPolicy> =
+        globalSettings
+            .observerFlow(DEVELOPMENT_SHADE_DISPLAY_AWARENESS)
+            .onStart { emit(Unit) }
+            .map {
+                val current = globalSettings.getString(DEVELOPMENT_SHADE_DISPLAY_AWARENESS)
+                for (policy in policies) {
+                    if (policy.name == current) return@map policy
+                }
+                globalSettings.putString(DEVELOPMENT_SHADE_DISPLAY_AWARENESS, defaultPolicy.name)
+                return@map defaultPolicy
+            }
+            .distinctUntilChanged()
+            .stateIn(bgScope, SharingStarted.WhileSubscribed(), defaultPolicy)
 
     override val displayId: StateFlow<Int> =
         policy
diff --git a/packages/SystemUI/src/com/android/systemui/shade/display/SpecificDisplayIdPolicy.kt b/packages/SystemUI/src/com/android/systemui/shade/display/DefaultDisplayShadePolicy.kt
similarity index 69%
rename from packages/SystemUI/src/com/android/systemui/shade/display/SpecificDisplayIdPolicy.kt
rename to packages/SystemUI/src/com/android/systemui/shade/display/DefaultDisplayShadePolicy.kt
index d43aad7..3819c6f 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/display/SpecificDisplayIdPolicy.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/display/DefaultDisplayShadePolicy.kt
@@ -21,12 +21,9 @@
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.StateFlow
 
-/** Policy to specify a display id explicitly. */
-open class SpecificDisplayIdPolicy(id: Int) : ShadeDisplayPolicy {
-    override val name: String = "display_${id}_policy"
+/** Policy to specify a default display explicitly. */
+class DefaultDisplayShadePolicy @Inject constructor() : ShadeDisplayPolicy {
+    override val name: String = "default_display"
 
-    override val displayId: StateFlow<Int> = MutableStateFlow(id)
+    override val displayId: StateFlow<Int> = MutableStateFlow(Display.DEFAULT_DISPLAY)
 }
-
-class DefaultDisplayShadePolicy @Inject constructor() :
-    SpecificDisplayIdPolicy(Display.DEFAULT_DISPLAY)
diff --git a/packages/SystemUI/src/com/android/systemui/shade/display/ShadeDisplayPolicy.kt b/packages/SystemUI/src/com/android/systemui/shade/display/ShadeDisplayPolicy.kt
index bb96b0b..17b5e5b 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/display/ShadeDisplayPolicy.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/display/ShadeDisplayPolicy.kt
@@ -23,6 +23,10 @@
 
 /** Describes the display the shade should be shown in. */
 interface ShadeDisplayPolicy {
+    /**
+     * String used to identify each policy and used to set policy via adb command. This value must
+     * match a value defined in the SettingsLib shade_display_awareness_values string array.
+     */
     val name: String
 
     /** The display id the shade should be at, according to this policy. */
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractor.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractor.kt
index 08c03e2..8d536ac 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractor.kt
@@ -27,6 +27,8 @@
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.scene.ui.view.WindowRootView
 import com.android.systemui.shade.ShadeDisplayAware
+import com.android.systemui.shade.ShadeTraceLogger.logMoveShadeWindowTo
+import com.android.systemui.shade.ShadeTraceLogger.traceReparenting
 import com.android.systemui.shade.data.repository.ShadeDisplaysRepository
 import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround
 import com.android.systemui.util.kotlin.getOrNull
@@ -68,6 +70,7 @@
     /** Tries to move the shade. If anything wrong happens, fails gracefully without crashing. */
     private suspend fun moveShadeWindowTo(destinationId: Int) {
         Log.d(TAG, "Trying to move shade window to display with id $destinationId")
+        logMoveShadeWindowTo(destinationId)
         // Why using the shade context here instead of the view's Display?
         // The context's display is updated before the view one, so it is a better indicator of
         // which display the shade is supposed to be at. The View display is updated after the first
@@ -83,7 +86,9 @@
             return
         }
         try {
-            withContext(mainThreadContext) { reparentToDisplayId(id = destinationId) }
+            withContext(mainThreadContext) {
+                traceReparenting { reparentToDisplayId(id = destinationId) }
+            }
         } catch (e: IllegalStateException) {
             Log.e(
                 TAG,
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorImpl.kt
index 50b5607..2d7476c 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorImpl.kt
@@ -16,7 +16,6 @@
 
 package com.android.systemui.shade.domain.interactor
 
-import com.android.keyguard.LockIconViewController
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.keyguard.shared.model.KeyguardState
@@ -38,7 +37,6 @@
     @Background private val backgroundScope: CoroutineScope,
     private val shadeInteractor: ShadeInteractor,
     private val sceneInteractor: SceneInteractor,
-    private val lockIconViewController: LockIconViewController,
     shadeRepository: ShadeRepository,
 ) : ShadeLockscreenInteractor {
 
@@ -61,7 +59,7 @@
     }
 
     override fun dozeTimeTick() {
-        lockIconViewController.dozeTimeTick()
+        // TODO("b/383591086") Implement replacement or delete
     }
 
     @Deprecated("Not supported by scenes")
diff --git a/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceModule.kt b/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceModule.kt
index ea4e065..3a5245d 100644
--- a/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceModule.kt
@@ -65,8 +65,8 @@
     @Binds
     @Named(LOCKSCREEN_SMARTSPACE_PRECONDITION)
     abstract fun bindSmartspacePrecondition(
-        lockscreenPrecondition: LockscreenPrecondition?
-    ): SmartspacePrecondition?
+        lockscreenPrecondition: LockscreenPrecondition
+    ): SmartspacePrecondition
 
     @BindsOptionalOf
     @Named(GLANCEABLE_HUB_SMARTSPACE_DATA_PLUGIN)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java
index d0dc7ac..2544323 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java
@@ -20,7 +20,6 @@
 import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG;
 
 import static com.android.systemui.Flags.fetchBookmarksXmlKeyboardShortcuts;
-import static com.android.systemui.Flags.validateKeyboardShortcutHelperIconUri;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -427,9 +426,7 @@
                 mKeySearchResultMap.put(SHORTCUT_SPECIFICAPP_INDEX, false);
             } else {
                 mCurrentAppPackageName = result.get(0).getPackageName();
-                if (validateKeyboardShortcutHelperIconUri()) {
-                    KeyboardShortcuts.sanitiseShortcuts(result);
-                }
+                KeyboardShortcuts.sanitiseShortcuts(result);
                 mSpecificAppGroup.addAll(
                         reMapToKeyboardShortcutMultiMappingGroup(result));
                 mKeySearchResultMap.put(SHORTCUT_SPECIFICAPP_INDEX, true);
@@ -445,9 +442,7 @@
         // Add specific Ime shortcuts
         if (result != null) {
             if (!result.isEmpty()) {
-                if (validateKeyboardShortcutHelperIconUri()) {
-                    KeyboardShortcuts.sanitiseShortcuts(result);
-                }
+                KeyboardShortcuts.sanitiseShortcuts(result);
                 mInputGroup.addAll(
                         reMapToKeyboardShortcutMultiMappingGroup(result));
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
index 766c391..2ed168a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
@@ -21,7 +21,6 @@
 import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG;
 
 import static com.android.systemui.Flags.fetchBookmarksXmlKeyboardShortcuts;
-import static com.android.systemui.Flags.validateKeyboardShortcutHelperIconUri;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -412,10 +411,7 @@
         mReceivedAppShortcutGroups =
                 result == null ? Collections.emptyList() : result;
 
-        if (validateKeyboardShortcutHelperIconUri()) {
-            sanitiseShortcuts(mReceivedAppShortcutGroups);
-        }
-
+        sanitiseShortcuts(mReceivedAppShortcutGroups);
         maybeMergeAndShowKeyboardShortcuts();
     }
 
@@ -423,10 +419,7 @@
         mReceivedImeShortcutGroups =
                 result == null ? Collections.emptyList() : result;
 
-        if (validateKeyboardShortcutHelperIconUri()) {
-            sanitiseShortcuts(mReceivedImeShortcutGroups);
-        }
-
+        sanitiseShortcuts(mReceivedImeShortcutGroups);
         maybeMergeAndShowKeyboardShortcuts();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
index 71efbab..3180a06 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
@@ -14,19 +14,49 @@
 
 package com.android.systemui.statusbar;
 
+import android.annotation.IntDef;
 import android.content.pm.UserInfo;
 import android.util.SparseArray;
 
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 public interface NotificationLockscreenUserManager {
     String PERMISSION_SELF = "com.android.systemui.permission.SELF";
     String NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION
             = "com.android.systemui.statusbar.work_challenge_unlocked_notification_action";
 
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(flag = true,
+            prefix = {"REDACTION_TYPE_"},
+            value = {
+                    REDACTION_TYPE_NONE,
+                    REDACTION_TYPE_PUBLIC,
+                    REDACTION_TYPE_SENSITIVE_CONTENT})
+    @interface RedactionType {}
+
+    /**
+     * Indicates that a notification requires no redaction
+     */
+    int REDACTION_TYPE_NONE = 0;
+
+    /**
+     * Indicates that a notification should have all content redacted, showing the public view.
+     * Overrides all other redaction types.
+     */
+    int REDACTION_TYPE_PUBLIC = 1;
+
+    /**
+     * Indicates that a notification should have its main content redacted, due to detected
+     * sensitive content, such as a One-Time Password
+     */
+    int REDACTION_TYPE_SENSITIVE_CONTENT = 1 << 1;
+
     /**
      * @param userId user Id
-     * @return true if we re on a secure lock screen
+     * @return true if we're on a secure lock screen
      */
     boolean isLockscreenPublicMode(int userId);
 
@@ -68,7 +98,13 @@
 
     void updatePublicMode();
 
-    boolean needsRedaction(NotificationEntry entry);
+    /**
+     * Determine what type of redaction is needed, if any. Returns REDACTION_TYPE_NONE if no
+     * redaction type is needed, REDACTION_TYPE_PUBLIC if private notifications are blocked, and
+     * REDACTION_TYPE_SENSITIVE_CONTENT if sensitive content is detected, and REDACTION_TYPE_PUBLIC
+     * doesn't apply.
+     */
+    @RedactionType int getRedactionType(NotificationEntry entry);
 
     /**
      * Has the given user chosen to allow their private (full) notifications to be shown even
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
index a79b78f..239257d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
@@ -654,8 +654,13 @@
         }
     }
 
-    /** @return true if the entry needs redaction when on the lockscreen. */
-    public boolean needsRedaction(NotificationEntry ent) {
+    /**
+     * Determine what type of redaction is needed, if any. Returns REDACTION_TYPE_NONE if no
+     * redaction type is needed, REDACTION_TYPE_PUBLIC if private notifications are blocked, and
+     * REDACTION_TYPE_SENSITIVE_CONTENT if sensitive content is detected, and REDACTION_TYPE_PUBLIC
+     * doesn't apply.
+     */
+    public @RedactionType int getRedactionType(NotificationEntry ent) {
         int userId = ent.getSbn().getUserId();
 
         boolean isCurrentUserRedactingNotifs =
@@ -675,13 +680,19 @@
                 ent.isNotificationVisibilityPrivate();
         boolean userForcesRedaction = packageHasVisibilityOverride(ent.getSbn().getKey());
 
-        if (keyguardPrivateNotifications()) {
-            return !mKeyguardAllowingNotifications || isNotifSensitive
-                    || userForcesRedaction || (notificationRequestsRedaction && isNotifRedacted);
-        } else {
-            return userForcesRedaction || isNotifSensitive
-                    || (notificationRequestsRedaction && isNotifRedacted);
+        if (userForcesRedaction) {
+            return REDACTION_TYPE_PUBLIC;
         }
+        if (notificationRequestsRedaction && isNotifRedacted) {
+            return REDACTION_TYPE_PUBLIC;
+        }
+        if (keyguardPrivateNotifications() && !mKeyguardAllowingNotifications) {
+            return REDACTION_TYPE_PUBLIC;
+        }
+        if (isNotifSensitive) {
+            return REDACTION_TYPE_SENSITIVE_CONTENT;
+        }
+        return REDACTION_TYPE_NONE;
     }
 
     private boolean packageHasVisibilityOverride(String key) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
index 684466a..3408f4f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
@@ -34,6 +34,7 @@
 import androidx.dynamicanimation.animation.SpringForce
 import com.android.app.animation.Interpolators
 import com.android.systemui.Dumpable
+import com.android.systemui.Flags.notificationShadeBlur
 import com.android.systemui.animation.ShadeInterpolation
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dump.DumpManager
@@ -53,7 +54,6 @@
 import javax.inject.Inject
 import kotlin.math.max
 import kotlin.math.sign
-import com.android.systemui.Flags.notificationShadeBlur
 
 /**
  * Responsible for blurring the notification shade window, and applying a zoom effect to the
@@ -212,19 +212,13 @@
             shadeRadius = 0f
         }
 
-        var zoomOut = MathUtils.saturate(blurUtils.ratioOfBlurRadius(shadeRadius))
         var blur = shadeRadius.toInt()
-
-        if (inSplitShade) {
-            zoomOut = 0f
-        }
-
+        val zoomOut = blurRadiusToZoomOut(blurRadius = shadeRadius)
         // Make blur be 0 if it is necessary to stop blur effect.
         if (scrimsVisible) {
             if (!notificationShadeBlur()) {
                 blur = 0
             }
-            zoomOut = 0f
         }
 
         if (!blurUtils.supportsBlursOnWindows()) {
@@ -237,24 +231,43 @@
         return Pair(blur, zoomOut)
     }
 
+    private fun blurRadiusToZoomOut(blurRadius: Float): Float {
+        var zoomOut = MathUtils.saturate(blurUtils.ratioOfBlurRadius(blurRadius))
+        if (inSplitShade) {
+            zoomOut = 0f
+        }
+
+        if (scrimsVisible) {
+            zoomOut = 0f
+        }
+        return zoomOut
+    }
+
+    private val shouldBlurBeOpaque: Boolean
+        get() = if (notificationShadeBlur()) false else scrimsVisible && !blursDisabledForAppLaunch
+
     /** Callback that updates the window blur value and is called only once per frame. */
     @VisibleForTesting
     val updateBlurCallback =
         Choreographer.FrameCallback {
             updateScheduled = false
-            val (blur, zoomOut) = computeBlurAndZoomOut()
-            val opaque = if (notificationShadeBlur()) false else scrimsVisible && !blursDisabledForAppLaunch
+            val (blur, zoomOutFromShadeRadius) = computeBlurAndZoomOut()
+            val opaque = shouldBlurBeOpaque
             Trace.traceCounter(Trace.TRACE_TAG_APP, "shade_blur_radius", blur)
             blurUtils.applyBlur(root.viewRootImpl, blur, opaque)
-            lastAppliedBlur = blur
-            wallpaperController.setNotificationShadeZoom(zoomOut)
-            listeners.forEach {
-                it.onWallpaperZoomOutChanged(zoomOut)
-                it.onBlurRadiusChanged(blur)
-            }
-            notificationShadeWindowController.setBackgroundBlurRadius(blur)
+            onBlurApplied(blur, zoomOutFromShadeRadius)
         }
 
+    private fun onBlurApplied(appliedBlurRadius: Int, zoomOutFromShadeRadius: Float) {
+        lastAppliedBlur = appliedBlurRadius
+        wallpaperController.setNotificationShadeZoom(zoomOutFromShadeRadius)
+        listeners.forEach {
+            it.onWallpaperZoomOutChanged(zoomOutFromShadeRadius)
+            it.onBlurRadiusChanged(appliedBlurRadius)
+        }
+        notificationShadeWindowController.setBackgroundBlurRadius(appliedBlurRadius)
+    }
+
     /** Animate blurs when unlocking. */
     private val keyguardStateCallback =
         object : KeyguardStateController.Callback {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/casttootherdevice/domain/interactor/MediaRouterChipInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/casttootherdevice/domain/interactor/MediaRouterChipInteractor.kt
index 229cef9..b3dbf29 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/casttootherdevice/domain/interactor/MediaRouterChipInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/casttootherdevice/domain/interactor/MediaRouterChipInteractor.kt
@@ -16,7 +16,6 @@
 
 package com.android.systemui.statusbar.chips.casttootherdevice.domain.interactor
 
-import android.media.projection.StopReason
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.log.LogBuffer
@@ -66,9 +65,7 @@
 
     /** Stops the currently active MediaRouter cast. */
     fun stopCasting() {
-        activeCastDevice.value?.let {
-            mediaRouterRepository.stopCasting(it, StopReason.STOP_PRIVACY_CHIP)
-        }
+        activeCastDevice.value?.let { mediaRouterRepository.stopCasting(it) }
     }
 
     companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractor.kt
index bbecde8..dff6f56 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractor.kt
@@ -137,7 +137,7 @@
             }
         }
 
-        return NotificationChipModel(key, statusBarChipIconView, whenTime, promotedContent)
+        return NotificationChipModel(key, statusBarChipIconView, promotedContent)
     }
 
     @AssistedFactory
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/model/NotificationChipModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/model/NotificationChipModel.kt
index 9f0638b..c6759da 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/model/NotificationChipModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/model/NotificationChipModel.kt
@@ -23,7 +23,5 @@
 data class NotificationChipModel(
     val key: String,
     val statusBarChipIconView: StatusBarIconView?,
-    // TODO(b/364653005): Use [PromotedNotificationContentModel.time] instead of a custom field.
-    val whenTime: Long,
     val promotedContent: PromotedNotificationContentModel,
 )
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 e2c886a..66af275 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
@@ -25,10 +25,13 @@
 import com.android.systemui.statusbar.chips.ui.model.ColorsModel
 import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
 import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
+import com.android.systemui.statusbar.notification.domain.interactor.HeadsUpNotificationInteractor
+import com.android.systemui.statusbar.notification.headsup.PinnedStatus
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.launch
 
 /** A view model for status bar chips for promoted ongoing notifications. */
@@ -38,18 +41,24 @@
 constructor(
     @Application private val applicationScope: CoroutineScope,
     private val notifChipsInteractor: StatusBarNotificationChipsInteractor,
+    headsUpNotificationInteractor: HeadsUpNotificationInteractor,
 ) {
     /**
      * 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.
      */
     val chips: Flow<List<OngoingActivityChipModel.Shown>> =
-        notifChipsInteractor.notificationChips.map { notifications ->
-            notifications.map { it.toActivityChipModel() }
+        combine(
+            notifChipsInteractor.notificationChips,
+            headsUpNotificationInteractor.statusBarHeadsUpState,
+        ) { notifications, headsUpState ->
+            notifications.map { it.toActivityChipModel(headsUpState) }
         }
 
     /** Converts the notification to the [OngoingActivityChipModel] object. */
-    private fun NotificationChipModel.toActivityChipModel(): OngoingActivityChipModel.Shown {
+    private fun NotificationChipModel.toActivityChipModel(
+        headsUpState: PinnedStatus
+    ): OngoingActivityChipModel.Shown {
         StatusBarNotifChips.assertInNewMode()
         val icon =
             if (this.statusBarChipIconView != null) {
@@ -74,15 +83,51 @@
                     )
                 }
             }
-        return OngoingActivityChipModel.Shown.ShortTimeDelta(
-            icon,
-            colors,
-            time = this.whenTime,
-            onClickListener,
-        )
-        // TODO(b/364653005): Use Notification.showWhen to determine if we should show the time.
-        // TODO(b/364653005): If Notification.shortCriticalText is set, use that instead of `when`.
-        // TODO(b/364653005): If the app that posted the notification is in the foreground, don't
-        // show that app's chip.
+
+        if (headsUpState == PinnedStatus.PinnedByUser) {
+            // If the user tapped the chip to show the HUN, we want to just show the icon because
+            // the HUN will show the rest of the information.
+            return OngoingActivityChipModel.Shown.IconOnly(icon, colors, onClickListener)
+        }
+
+        if (this.promotedContent.shortCriticalText != null) {
+            return OngoingActivityChipModel.Shown.Text(
+                icon,
+                colors,
+                this.promotedContent.shortCriticalText,
+                onClickListener,
+            )
+        }
+
+        if (this.promotedContent.time == null) {
+            return OngoingActivityChipModel.Shown.IconOnly(icon, colors, onClickListener)
+        }
+        when (this.promotedContent.time.mode) {
+            PromotedNotificationContentModel.When.Mode.BasicTime -> {
+                return OngoingActivityChipModel.Shown.ShortTimeDelta(
+                    icon,
+                    colors,
+                    time = this.promotedContent.time.time,
+                    onClickListener,
+                )
+            }
+            PromotedNotificationContentModel.When.Mode.CountUp -> {
+                return OngoingActivityChipModel.Shown.Timer(
+                    icon,
+                    colors,
+                    startTimeMs = this.promotedContent.time.time,
+                    onClickListener,
+                )
+            }
+            PromotedNotificationContentModel.When.Mode.CountDown -> {
+                // TODO(b/364653005): Support CountDown.
+                return OngoingActivityChipModel.Shown.Timer(
+                    icon,
+                    colors,
+                    startTimeMs = this.promotedContent.time.time,
+                    onClickListener,
+                )
+            }
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractor.kt
index 0b5e669..f5952f4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractor.kt
@@ -16,7 +16,6 @@
 
 package com.android.systemui.statusbar.chips.screenrecord.domain.interactor
 
-import android.media.projection.StopReason
 import com.android.systemui.Flags
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
@@ -141,7 +140,7 @@
 
     /** Stops the recording. */
     fun stopRecording() {
-        scope.launch { screenRecordRepository.stopRecording(StopReason.STOP_PRIVACY_CHIP) }
+        scope.launch { screenRecordRepository.stopRecording() }
     }
 
     companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/OngoingActivityChipBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/OngoingActivityChipBinder.kt
index cf69d40..0c4c1a7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/OngoingActivityChipBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/OngoingActivityChipBinder.kt
@@ -310,9 +310,13 @@
 
     private fun View.setBackgroundPaddingForEmbeddedPaddingIcon() {
         val sidePadding =
-            context.resources.getDimensionPixelSize(
-                R.dimen.ongoing_activity_chip_side_padding_for_embedded_padding_icon
-            )
+            if (StatusBarNotifChips.isEnabled) {
+                0
+            } else {
+                context.resources.getDimensionPixelSize(
+                    R.dimen.ongoing_activity_chip_side_padding_for_embedded_padding_icon
+                )
+            }
         setPaddingRelative(sidePadding, paddingTop, sidePadding, paddingBottom)
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt
index 2dce4e3..18217d7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt
@@ -116,7 +116,8 @@
             override val colors: ColorsModel,
             // TODO(b/361346412): Enforce a max length requirement?
             val text: String,
-        ) : Shown(icon, colors, onClickListener = null) {
+            override val onClickListener: View.OnClickListener? = null,
+        ) : Shown(icon, colors, onClickListener) {
             override val logName = "Shown.Text"
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt
index 2588c7a..46c84fbc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt
@@ -37,6 +37,7 @@
 import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController
 import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallLog
 import com.android.systemui.statusbar.phone.ongoingcall.StatusBarChipsModernization
+import com.android.systemui.statusbar.phone.ongoingcall.domain.interactor.OngoingCallInteractor
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.statusbar.ui.SystemBarUtilsProxyImpl
 import com.android.systemui.statusbar.window.MultiDisplayStatusBarWindowControllerStore
@@ -101,6 +102,19 @@
 
         @Provides
         @SysUISingleton
+        @IntoMap
+        @ClassKey(OngoingCallInteractor::class)
+        fun ongoingCallInteractor(
+            interactor: OngoingCallInteractor
+        ): CoreStartable =
+            if (StatusBarChipsModernization.isEnabled) {
+                interactor
+            } else {
+                CoreStartable.NOP
+            }
+
+        @Provides
+        @SysUISingleton
         fun lightBarController(store: LightBarControllerStore): LightBarController {
             return store.defaultDisplay
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModePerDisplayRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModePerDisplayRepository.kt
index cc91e2d..22c37df 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModePerDisplayRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModePerDisplayRepository.kt
@@ -37,6 +37,7 @@
 import com.android.systemui.statusbar.phone.LetterboxAppearanceCalculator
 import com.android.systemui.statusbar.phone.StatusBarBoundsProvider
 import com.android.systemui.statusbar.phone.fragment.dagger.HomeStatusBarComponent
+import com.android.systemui.statusbar.phone.ongoingcall.StatusBarChipsModernization
 import com.android.systemui.statusbar.phone.ongoingcall.data.repository.OngoingCallRepository
 import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
 import dagger.assisted.Assisted
@@ -89,6 +90,9 @@
     /** The current mode of the status bar. */
     val statusBarMode: StateFlow<StatusBarMode>
 
+    /** Whether the status bar is forced to be visible because of an ongoing call */
+    val ongoingProcessRequiresStatusBarVisible: StateFlow<Boolean>
+
     /**
      * Requests for the status bar to be shown transiently.
      *
@@ -110,6 +114,12 @@
      * if needed.
      */
     fun stop()
+
+    /**
+     * Called when an ongoing process needs to prevent the status bar from being hidden in any
+     * state.
+     */
+    fun setOngoingProcessRequiresStatusBarVisible(requiredVisible: Boolean)
 }
 
 class StatusBarModePerDisplayRepositoryImpl
@@ -195,6 +205,16 @@
         statusBarBoundsProvider.addChangeListener(listener)
     }
 
+    private val _ongoingProcessRequiresStatusBarVisible = MutableStateFlow(false)
+    override val ongoingProcessRequiresStatusBarVisible =
+        _ongoingProcessRequiresStatusBarVisible.asStateFlow()
+
+    override fun setOngoingProcessRequiresStatusBarVisible(
+        requiredVisible: Boolean
+    ) {
+        _ongoingProcessRequiresStatusBarVisible.value = requiredVisible
+    }
+
     override val isInFullscreenMode: StateFlow<Boolean> =
         _originalStatusBarAttributes
             .map { params ->
@@ -235,16 +255,28 @@
                 isTransientShown,
                 isInFullscreenMode,
                 ongoingCallRepository.ongoingCallState,
-            ) { modifiedAttributes, isTransientShown, isInFullscreenMode, ongoingCallState ->
+                _ongoingProcessRequiresStatusBarVisible,
+            ) {
+                modifiedAttributes,
+                isTransientShown,
+                isInFullscreenMode,
+                ongoingCallStateLegacy,
+                ongoingProcessRequiresStatusBarVisible ->
                 if (modifiedAttributes == null) {
                     null
                 } else {
+                    val hasOngoingCall =
+                        if (StatusBarChipsModernization.isEnabled) {
+                            ongoingProcessRequiresStatusBarVisible
+                        } else {
+                            ongoingCallStateLegacy is OngoingCallModel.InCall
+                        }
                     val statusBarMode =
                         toBarMode(
                             modifiedAttributes.appearance,
                             isTransientShown,
                             isInFullscreenMode,
-                            hasOngoingCall = ongoingCallState is OngoingCallModel.InCall,
+                            hasOngoingCall,
                         )
                     StatusBarAppearance(
                         statusBarMode,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index 6b84b6d..c38b84b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -666,7 +666,16 @@
     }
 
     public boolean isRowPinned() {
-        return row != null && row.isPinned();
+        return getPinnedStatus().isPinned();
+    }
+
+    /** Returns this notification's current pinned status. */
+    public PinnedStatus getPinnedStatus() {
+        if (row != null) {
+            return row.getPinnedStatus();
+        } else {
+            return PinnedStatus.NotPinned;
+        }
     }
 
     /**
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 0269b16..eb6ec9f 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
@@ -43,6 +43,7 @@
 import com.android.systemui.statusbar.notification.dagger.IncomingHeader
 import com.android.systemui.statusbar.notification.headsup.HeadsUpManager
 import com.android.systemui.statusbar.notification.headsup.OnHeadsUpChangedListener
+import com.android.systemui.statusbar.notification.headsup.PinnedStatus
 import com.android.systemui.statusbar.notification.interruption.HeadsUpViewBinder
 import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProvider
 import com.android.systemui.statusbar.notification.logKey
@@ -145,6 +146,7 @@
                 // heads-up is considered to be the top notification.
                 shouldHeadsUpEver = true,
                 shouldHeadsUpAgain = true,
+                isPinnedByUser = true,
                 isHeadsUpEntry = mHeadsUpManager.isHeadsUpEntry(entry.key),
                 isBinding = isEntryBinding(entry),
             )
@@ -155,8 +157,8 @@
         }
     }
 
-    private fun onHeadsUpViewBound(entry: NotificationEntry) {
-        mHeadsUpManager.showNotification(entry)
+    private fun onHeadsUpViewBound(entry: NotificationEntry, isPinnedByUser: Boolean) {
+        mHeadsUpManager.showNotification(entry, isPinnedByUser)
         mEntriesBindingUntil.remove(entry.key)
     }
 
@@ -424,6 +426,7 @@
 
     private fun handlePostedEntry(posted: PostedEntry, hunMutator: HunMutator, scenario: String) {
         mLogger.logPostedEntryWillEvaluate(posted, scenario)
+
         if (posted.wasAdded) {
             if (posted.shouldHeadsUpEver) {
                 bindForAsyncHeadsUp(posted)
@@ -437,7 +440,17 @@
                     // If showing heads up, we need to post an update. Otherwise we're still
                     // binding, and we can just let that finish.
                     if (posted.isHeadsUpEntry) {
-                        hunMutator.updateNotification(posted.key, posted.shouldHeadsUpAgain)
+                        val pinnedStatus =
+                            if (posted.shouldHeadsUpAgain) {
+                                if (StatusBarNotifChips.isEnabled && posted.isPinnedByUser) {
+                                    PinnedStatus.PinnedByUser
+                                } else {
+                                    PinnedStatus.PinnedBySystem
+                                }
+                            } else {
+                                PinnedStatus.NotPinned
+                            }
+                        hunMutator.updateNotification(posted.key, pinnedStatus)
                     }
                 } else {
                     if (posted.isHeadsUpEntry) {
@@ -461,10 +474,11 @@
     }
 
     private fun bindForAsyncHeadsUp(posted: PostedEntry) {
+        val isPinnedByUser = StatusBarNotifChips.isEnabled && posted.isPinnedByUser
         // TODO: Add a guarantee to bindHeadsUpView of some kind of callback if the bind is
         //  cancelled so that we don't need to have this sad timeout hack.
         mEntriesBindingUntil[posted.key] = mNow + BIND_TIMEOUT
-        mHeadsUpViewBinder.bindHeadsUpView(posted.entry, this::onHeadsUpViewBound)
+        mHeadsUpViewBinder.bindHeadsUpView(posted.entry, isPinnedByUser, this::onHeadsUpViewBound)
     }
 
     private val mNotifCollectionListener =
@@ -906,6 +920,7 @@
         var wasUpdated: Boolean,
         var shouldHeadsUpEver: Boolean,
         var shouldHeadsUpAgain: Boolean,
+        var isPinnedByUser: Boolean = false,
         var isHeadsUpEntry: Boolean,
         var isBinding: Boolean,
     ) {
@@ -943,7 +958,7 @@
 
 /** Mutates the HeadsUp state of notifications. */
 private interface HunMutator {
-    fun updateNotification(key: String, shouldHeadsUpAgain: Boolean)
+    fun updateNotification(key: String, requestedPinnedStatus: PinnedStatus)
 
     fun removeNotification(key: String, releaseImmediately: Boolean)
 }
@@ -955,8 +970,8 @@
 private class HunMutatorImpl(private val headsUpManager: HeadsUpManager) : HunMutator {
     private val deferred = mutableListOf<Pair<String, Boolean>>()
 
-    override fun updateNotification(key: String, shouldHeadsUpAgain: Boolean) {
-        headsUpManager.updateNotification(key, shouldHeadsUpAgain)
+    override fun updateNotification(key: String, requestedPinnedStatus: PinnedStatus) {
+        headsUpManager.updateNotification(key, requestedPinnedStatus)
     }
 
     override fun removeNotification(key: String, releaseImmediately: Boolean) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
index f75163d..2ecce1f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
@@ -416,7 +416,7 @@
                 /* showSnooze = */ adjustment.isSnoozeEnabled(),
                 /* isChildInGroup = */ adjustment.isChildInGroup(),
                 /* isGroupSummary = */ adjustment.isGroupSummary(),
-                /* needsRedaction = */ adjustment.getNeedsRedaction()
+                /* needsRedaction = */ adjustment.getRedactionType()
         );
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt
index 04458f3..1875e7e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt
@@ -30,6 +30,7 @@
 import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.statusbar.NotificationLockscreenUserManager
+import com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTION_TYPE_NONE
 import com.android.systemui.statusbar.StatusBarState
 import com.android.systemui.statusbar.notification.DynamicPrivacyController
 import com.android.systemui.statusbar.notification.collection.GroupEntry
@@ -211,7 +212,8 @@
                 screenshareNotificationHiding() &&
                     sensitiveNotificationProtectionController.shouldProtectNotification(entry)
 
-            val needsRedaction = lockscreenUserManager.needsRedaction(entry)
+            val needsRedaction =
+                lockscreenUserManager.getRedactionType(entry) != REDACTION_TYPE_NONE
             val isSensitive = userPublic && needsRedaction
             entry.setSensitive(isSensitive || shouldProtectNotification, deviceSensitive)
             if (screenshareNotificationHiding()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifInflater.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifInflater.kt
index ff72888..ff9d533 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifInflater.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifInflater.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar.notification.collection.inflation
 
+import com.android.systemui.statusbar.NotificationLockscreenUserManager.RedactionType
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
 import com.android.systemui.statusbar.notification.collection.render.NotifViewController
 
@@ -61,6 +62,6 @@
         val showSnooze: Boolean,
         val isChildInGroup: Boolean = false,
         val isGroupSummary: Boolean = false,
-        val needsRedaction: Boolean,
+        @RedactionType val redactionType: Int,
     )
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustment.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustment.kt
index e70fb6b..331ef1c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustment.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustment.kt
@@ -20,22 +20,24 @@
 import android.app.RemoteInput
 import android.graphics.drawable.Icon
 import android.text.TextUtils
+import com.android.systemui.statusbar.NotificationLockscreenUserManager.RedactionType
 import com.android.systemui.statusbar.notification.row.shared.AsyncGroupHeaderViewInflation
 import com.android.systemui.statusbar.notification.row.shared.AsyncHybridViewInflation
 
 /**
  * An immutable object which contains minimal state extracted from an entry that represents state
- * which can change without a direct app update (e.g. with a ranking update).
- * Diffing two entries determines if view re-inflation is needed.
+ * which can change without a direct app update (e.g. with a ranking update). Diffing two entries
+ * determines if view re-inflation is needed.
  */
-class NotifUiAdjustment internal constructor(
+class NotifUiAdjustment
+internal constructor(
     val key: String,
     val smartActions: List<Notification.Action>,
     val smartReplies: List<CharSequence>,
     val isConversation: Boolean,
     val isSnoozeEnabled: Boolean,
     val isMinimized: Boolean,
-    val needsRedaction: Boolean,
+    @RedactionType val redactionType: Int,
     val isChildInGroup: Boolean,
     val isGroupSummary: Boolean,
 ) {
@@ -43,65 +45,72 @@
         @JvmStatic
         fun needReinflate(
             oldAdjustment: NotifUiAdjustment,
-            newAdjustment: NotifUiAdjustment
-        ): Boolean = when {
-            oldAdjustment === newAdjustment -> false
-            oldAdjustment.isConversation != newAdjustment.isConversation -> true
-            oldAdjustment.isSnoozeEnabled != newAdjustment.isSnoozeEnabled -> true
-            oldAdjustment.isMinimized != newAdjustment.isMinimized -> true
-            oldAdjustment.needsRedaction != newAdjustment.needsRedaction -> true
-            areDifferent(oldAdjustment.smartActions, newAdjustment.smartActions) -> true
-            newAdjustment.smartReplies != oldAdjustment.smartReplies -> true
-            AsyncHybridViewInflation.isEnabled &&
-                    !oldAdjustment.isChildInGroup && newAdjustment.isChildInGroup -> true
-            AsyncGroupHeaderViewInflation.isEnabled &&
-                !oldAdjustment.isGroupSummary && newAdjustment.isGroupSummary -> true
-            else -> false
-        }
+            newAdjustment: NotifUiAdjustment,
+        ): Boolean =
+            when {
+                oldAdjustment === newAdjustment -> false
+                oldAdjustment.isConversation != newAdjustment.isConversation -> true
+                oldAdjustment.isSnoozeEnabled != newAdjustment.isSnoozeEnabled -> true
+                oldAdjustment.isMinimized != newAdjustment.isMinimized -> true
+                oldAdjustment.redactionType != newAdjustment.redactionType -> true
+                areDifferent(oldAdjustment.smartActions, newAdjustment.smartActions) -> true
+                newAdjustment.smartReplies != oldAdjustment.smartReplies -> true
+                AsyncHybridViewInflation.isEnabled &&
+                    !oldAdjustment.isChildInGroup &&
+                    newAdjustment.isChildInGroup -> true
+                AsyncGroupHeaderViewInflation.isEnabled &&
+                    !oldAdjustment.isGroupSummary &&
+                    newAdjustment.isGroupSummary -> true
+                else -> false
+            }
 
         private fun areDifferent(
             first: List<Notification.Action>,
-            second: List<Notification.Action>
-        ): Boolean = when {
-            first === second -> false
-            first.size != second.size -> true
-            else -> first.asSequence().zip(second.asSequence()).any {
-                (!TextUtils.equals(it.first.title, it.second.title)) ||
-                    (areDifferent(it.first.getIcon(), it.second.getIcon())) ||
-                    (it.first.actionIntent != it.second.actionIntent) ||
-                    (areDifferent(it.first.remoteInputs, it.second.remoteInputs))
+            second: List<Notification.Action>,
+        ): Boolean =
+            when {
+                first === second -> false
+                first.size != second.size -> true
+                else ->
+                    first.asSequence().zip(second.asSequence()).any {
+                        (!TextUtils.equals(it.first.title, it.second.title)) ||
+                            (areDifferent(it.first.getIcon(), it.second.getIcon())) ||
+                            (it.first.actionIntent != it.second.actionIntent) ||
+                            (areDifferent(it.first.remoteInputs, it.second.remoteInputs))
+                    }
             }
-        }
 
-        private fun areDifferent(first: Icon?, second: Icon?): Boolean = when {
-            first === second -> false
-            first == null || second == null -> true
-            else -> !first.sameAs(second)
-        }
-
-        private fun areDifferent(
-            first: Array<RemoteInput>?,
-            second: Array<RemoteInput>?
-        ): Boolean = when {
-            first === second -> false
-            first == null || second == null -> true
-            first.size != second.size -> true
-            else -> first.asSequence().zip(second.asSequence()).any {
-                (!TextUtils.equals(it.first.label, it.second.label)) ||
-                    (areDifferent(it.first.choices, it.second.choices))
+        private fun areDifferent(first: Icon?, second: Icon?): Boolean =
+            when {
+                first === second -> false
+                first == null || second == null -> true
+                else -> !first.sameAs(second)
             }
-        }
+
+        private fun areDifferent(first: Array<RemoteInput>?, second: Array<RemoteInput>?): Boolean =
+            when {
+                first === second -> false
+                first == null || second == null -> true
+                first.size != second.size -> true
+                else ->
+                    first.asSequence().zip(second.asSequence()).any {
+                        (!TextUtils.equals(it.first.label, it.second.label)) ||
+                            (areDifferent(it.first.choices, it.second.choices))
+                    }
+            }
 
         private fun areDifferent(
             first: Array<CharSequence>?,
-            second: Array<CharSequence>?
-        ): Boolean = when {
-            first === second -> false
-            first == null || second == null -> true
-            first.size != second.size -> true
-            else -> first.asSequence().zip(second.asSequence()).any {
-                !TextUtils.equals(it.first, it.second)
+            second: Array<CharSequence>?,
+        ): Boolean =
+            when {
+                first === second -> false
+                first == null || second == null -> true
+                first.size != second.size -> true
+                else ->
+                    first.asSequence().zip(second.asSequence()).any {
+                        !TextUtils.equals(it.first, it.second)
+                    }
             }
-        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt
index 4c82bc1..97e55c1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt
@@ -27,6 +27,7 @@
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.settings.UserTracker
 import com.android.systemui.statusbar.NotificationLockscreenUserManager
+import com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTION_TYPE_PUBLIC
 import com.android.systemui.statusbar.notification.collection.GroupEntry
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
 import com.android.systemui.statusbar.notification.collection.provider.SectionStyleProvider
@@ -79,7 +80,7 @@
             secureSettings.registerContentObserverForUserSync(
                 SHOW_NOTIFICATION_SNOOZE,
                 settingsObserver,
-                UserHandle.USER_ALL
+                UserHandle.USER_ALL,
             )
         }
         dirtyListeners.addIfAbsent(listener)
@@ -140,10 +141,15 @@
             isConversation = entry.ranking.isConversation,
             isSnoozeEnabled = isSnoozeSettingsEnabled && !entry.isCanceled,
             isMinimized = isEntryMinimized(entry),
-            needsRedaction =
-                lockscreenUserManager.needsRedaction(entry) ||
-                    (screenshareNotificationHiding() &&
-                        sensitiveNotifProtectionController.shouldProtectNotification(entry)),
+            redactionType =
+                if (
+                    screenshareNotificationHiding() &&
+                        sensitiveNotifProtectionController.shouldProtectNotification(entry)
+                ) {
+                    REDACTION_TYPE_PUBLIC
+                } else {
+                    lockscreenUserManager.getRedactionType(entry)
+                },
             isChildInGroup = entry.hasEverBeenGroupChild(),
             isGroupSummary = entry.hasEverBeenGroupSummary(),
         )
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
index e6d22b0..80e8f55 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
@@ -16,7 +16,7 @@
 
 package com.android.systemui.statusbar.notification.collection.inflation;
 
-import static com.android.server.notification.Flags.screenshareNotificationHiding;
+import static com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTION_TYPE_NONE;
 import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED;
 import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_EXPANDED;
 import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_PUBLIC;
@@ -256,9 +256,8 @@
         params.requireContentViews(FLAG_CONTENT_VIEW_EXPANDED);
         params.setUseIncreasedCollapsedHeight(useIncreasedCollapsedHeight);
         params.setUseMinimized(isMinimized);
-        boolean needsRedaction = screenshareNotificationHiding()
-                ? inflaterParams.getNeedsRedaction()
-                : mNotificationLockscreenUserManager.needsRedaction(entry);
+        // TODO b/358403414: use the different types of redaction
+        boolean needsRedaction = inflaterParams.getRedactionType() != REDACTION_TYPE_NONE;
 
         if (needsRedaction) {
             params.requireContentViews(FLAG_CONTENT_VIEW_PUBLIC);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractor.kt
index 64e78e4..75c7d2d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractor.kt
@@ -25,6 +25,7 @@
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
 import com.android.systemui.statusbar.notification.data.repository.HeadsUpRepository
 import com.android.systemui.statusbar.notification.data.repository.HeadsUpRowRepository
+import com.android.systemui.statusbar.notification.headsup.PinnedStatus
 import com.android.systemui.statusbar.notification.shared.HeadsUpRowKey
 import javax.inject.Inject
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -97,19 +98,22 @@
         }
     }
 
-    /** Are there any pinned heads up rows to display? */
-    val hasPinnedRows: Flow<Boolean> =
+    /** What [PinnedStatus] does the top row have? */
+    private val topPinnedStatus: Flow<PinnedStatus> =
         headsUpRepository.activeHeadsUpRows.flatMapLatest { rows ->
             if (rows.isNotEmpty()) {
                 combine(rows.map { it.pinnedStatus }) { pinnedStatus ->
-                    pinnedStatus.any { it.isPinned }
+                    pinnedStatus.firstOrNull { it.isPinned } ?: PinnedStatus.NotPinned
                 }
             } else {
                 // if the set is empty, there are no flows to combine
-                flowOf(false)
+                flowOf(PinnedStatus.NotPinned)
             }
         }
 
+    /** Are there any pinned heads up rows to display? */
+    val hasPinnedRows: Flow<Boolean> = topPinnedStatus.map { it.isPinned }
+
     val isHeadsUpOrAnimatingAway: Flow<Boolean> by lazy {
         if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) {
             flowOf(false)
@@ -138,9 +142,14 @@
             }
         }
 
-    val showHeadsUpStatusBar =
-        combine(hasPinnedRows, canShowHeadsUp) { hasPinnedRows, canShowHeadsUp ->
-            hasPinnedRows && canShowHeadsUp
+    /** Emits the pinned notification state as it relates to the status bar. */
+    val statusBarHeadsUpState: Flow<PinnedStatus> =
+        combine(topPinnedStatus, canShowHeadsUp) { topPinnedStatus, canShowHeadsUp ->
+            if (canShowHeadsUp) {
+                topPinnedStatus
+            } else {
+                PinnedStatus.NotPinned
+            }
         }
 
     fun headsUpRow(key: HeadsUpRowKey): HeadsUpRowInteractor =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManager.kt
index 424a3c5..95234da 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManager.kt
@@ -88,6 +88,12 @@
     /** Returns whether there are any pinned Heads Up Notifications or not. */
     fun hasPinnedHeadsUp(): Boolean
 
+    /**
+     * Returns the status of the top Heads Up Notification, or returns [PinnedStatus.NotPinned] if
+     * there is no pinned HUN.
+     */
+    fun pinnedHeadsUpStatus(): PinnedStatus
+
     /** Returns whether or not the given notification is managed by this manager. */
     fun isHeadsUpEntry(key: String): Boolean
 
@@ -204,8 +210,10 @@
      * the notification to be managed.
      *
      * @param entry entry to show
+     * @param isPinnedByUser true if the notification was pinned by the user and false if the
+     *   notification was pinned by the system.
      */
-    fun showNotification(entry: NotificationEntry)
+    fun showNotification(entry: NotificationEntry, isPinnedByUser: Boolean = false)
 
     fun snooze()
 
@@ -216,7 +224,15 @@
      */
     fun unpinAll(userUnPinned: Boolean)
 
-    fun updateNotification(key: String, shouldHeadsUpAgain: Boolean)
+    /**
+     * Called when the notification state has been updated.
+     *
+     * @param key the key of the entry that was updated
+     * @param requestedPinnedStatus whether and how the notification should be pinned. If equal to
+     *   [PinnedStatus.NotPinned], the notification won't show again. Otherwise, the notification
+     *   should show again and will force reevaluation of removal time.
+     */
+    fun updateNotification(key: String, requestedPinnedStatus: PinnedStatus)
 
     fun onEntryAnimatingAwayEnded(entry: NotificationEntry)
 }
@@ -262,6 +278,8 @@
 
     override fun hasPinnedHeadsUp() = false
 
+    override fun pinnedHeadsUpStatus() = PinnedStatus.NotPinned
+
     override fun isHeadsUpEntry(key: String) = false
 
     override fun isHeadsUpAnimatingAwayValue() = false
@@ -306,13 +324,13 @@
 
     override fun shouldSwallowClick(key: String): Boolean = false
 
-    override fun showNotification(entry: NotificationEntry) {}
+    override fun showNotification(entry: NotificationEntry, isPinnedByUser: Boolean) {}
 
     override fun snooze() {}
 
     override fun unpinAll(userUnPinned: Boolean) {}
 
-    override fun updateNotification(key: String, alert: Boolean) {}
+    override fun updateNotification(key: String, requestedPinnedStatus: PinnedStatus) {}
 
     override fun onEntryAnimatingAwayEnded(entry: NotificationEntry) {}
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImpl.java
index d0c02f7..6756077 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImpl.java
@@ -45,7 +45,9 @@
 import com.android.systemui.shade.ShadeDisplayAware;
 import com.android.systemui.shade.domain.interactor.ShadeInteractor;
 import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.coordinator.HeadsUpCoordinator;
 import com.android.systemui.statusbar.notification.collection.provider.OnReorderingAllowedListener;
 import com.android.systemui.statusbar.notification.collection.provider.OnReorderingBannedListener;
 import com.android.systemui.statusbar.notification.collection.provider.VisualStabilityProvider;
@@ -64,6 +66,11 @@
 import com.android.systemui.util.settings.GlobalSettings;
 import com.android.systemui.util.time.SystemClock;
 
+import kotlinx.coroutines.flow.Flow;
+import kotlinx.coroutines.flow.MutableStateFlow;
+import kotlinx.coroutines.flow.StateFlow;
+import kotlinx.coroutines.flow.StateFlowKt;
+
 import org.jetbrains.annotations.NotNull;
 
 import java.io.PrintWriter;
@@ -77,11 +84,6 @@
 
 import javax.inject.Inject;
 
-import kotlinx.coroutines.flow.Flow;
-import kotlinx.coroutines.flow.MutableStateFlow;
-import kotlinx.coroutines.flow.StateFlow;
-import kotlinx.coroutines.flow.StateFlowKt;
-
 /**
  * A manager which handles heads up notifications which is a special mode where
  * they simply peek from the top of the screen.
@@ -92,14 +94,15 @@
     private static final String TAG = "BaseHeadsUpManager";
     private static final String SETTING_HEADS_UP_SNOOZE_LENGTH_MS = "heads_up_snooze_length_ms";
     private static final String REASON_REORDER_ALLOWED = "mOnReorderingAllowedListener";
-    protected final ListenerSet<OnHeadsUpChangedListener> mListeners = new ListenerSet<>();
+    private final ListenerSet<OnHeadsUpChangedListener> mListeners = new ListenerSet<>();
 
-    protected final Context mContext;
+    private final Context mContext;
 
-    protected int mTouchAcceptanceDelay;
-    protected int mSnoozeLengthMs;
-    protected boolean mHasPinnedNotification;
-    protected int mUser;
+    private final int mTouchAcceptanceDelay;
+    private int mSnoozeLengthMs;
+    private boolean mHasPinnedNotification;
+    private PinnedStatus mPinnedNotificationStatus = PinnedStatus.NotPinned;
+    private int mUser;
 
     private final ArrayMap<String, Long> mSnoozedPackages;
     private final AccessibilityManagerWrapper mAccessibilityMgr;
@@ -111,13 +114,14 @@
     private final List<OnHeadsUpPhoneListenerChange> mHeadsUpPhoneListeners = new ArrayList<>();
     private final VisualStabilityProvider mVisualStabilityProvider;
 
-    protected final SystemClock mSystemClock;
-    protected final ArrayMap<String, HeadsUpEntry> mHeadsUpEntryMap = new ArrayMap<>();
-    protected final HeadsUpManagerLogger mLogger;
-    protected int mMinimumDisplayTime;
-    protected int mStickyForSomeTimeAutoDismissTime;
-    protected int mAutoDismissTime;
-    protected DelayableExecutor mExecutor;
+    private final SystemClock mSystemClock;
+    @VisibleForTesting
+    final ArrayMap<String, HeadsUpEntry> mHeadsUpEntryMap = new ArrayMap<>();
+    private final HeadsUpManagerLogger mLogger;
+    private final int mMinimumDisplayTime;
+    private final int mStickyForSomeTimeAutoDismissTime;
+    private final int mAutoDismissTime;
+    private final DelayableExecutor mExecutor;
 
     private final int mExtensionTime;
 
@@ -131,7 +135,7 @@
     private final HashSet<String> mSwipedOutKeys = new HashSet<>();
     private final HashSet<NotificationEntry> mEntriesToRemoveAfterExpand = new HashSet<>();
     @VisibleForTesting
-    public final ArraySet<NotificationEntry> mEntriesToRemoveWhenReorderingAllowed
+    final ArraySet<NotificationEntry> mEntriesToRemoveWhenReorderingAllowed
             = new ArraySet<>();
 
     private boolean mReleaseOnExpandFinish;
@@ -303,28 +307,28 @@
                 + resources.getDimensionPixelSize(R.dimen.heads_up_status_bar_padding);
     }
 
-    /**
-     * Called when posting a new notification that should appear on screen.
-     * Adds the notification to be managed.
-     * @param entry entry to show
-     */
     @Override
-    public void showNotification(@NonNull NotificationEntry entry) {
+    public void showNotification(
+            @NonNull NotificationEntry entry, boolean isPinnedByUser) {
         HeadsUpEntry headsUpEntry = createHeadsUpEntry(entry);
 
-        mLogger.logShowNotificationRequest(entry);
+        mLogger.logShowNotificationRequest(entry, isPinnedByUser);
 
         Runnable runnable = () -> {
-            mLogger.logShowNotification(entry);
+            mLogger.logShowNotification(entry, isPinnedByUser);
 
             // Add new entry and begin managing it
             mHeadsUpEntryMap.put(entry.getKey(), headsUpEntry);
-            onEntryAdded(headsUpEntry);
+            PinnedStatus requestedPinnedStatus =
+                    isPinnedByUser
+                            ? PinnedStatus.PinnedByUser
+                            : PinnedStatus.PinnedBySystem;
+            onEntryAdded(headsUpEntry, requestedPinnedStatus);
             // TODO(b/328390331) move accessibility events to the view layer
             entry.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
             entry.setIsHeadsUpEntry(true);
 
-            updateNotificationInternal(entry.getKey(), true /* shouldHeadsUpAgain */);
+            updateNotificationInternal(entry.getKey(), requestedPinnedStatus);
             entry.setInterruption();
         };
         mAvalancheController.update(headsUpEntry, runnable, "showNotification");
@@ -375,25 +379,20 @@
         return false;
     }
 
-    /**
-     * Called when the notification state has been updated.
-     * @param key the key of the entry that was updated
-     * @param shouldHeadsUpAgain whether the notification should show again and force reevaluation
-     *                           of removal time
-     */
-    public void updateNotification(@NonNull String key, boolean shouldHeadsUpAgain) {
+    @Override
+    public void updateNotification(
+            @NonNull String key, @NonNull PinnedStatus requestedPinnedStatus) {
         HeadsUpEntry headsUpEntry = mHeadsUpEntryMap.get(key);
-        mLogger.logUpdateNotificationRequest(key, shouldHeadsUpAgain, headsUpEntry != null);
+        mLogger.logUpdateNotificationRequest(key, requestedPinnedStatus, headsUpEntry != null);
 
-        Runnable runnable = () -> {
-            updateNotificationInternal(key, shouldHeadsUpAgain);
-        };
+        Runnable runnable = () -> updateNotificationInternal(key, requestedPinnedStatus);
         mAvalancheController.update(headsUpEntry, runnable, "updateNotification");
     }
 
-    private void updateNotificationInternal(@NonNull String key, boolean shouldHeadsUpAgain) {
+    private void updateNotificationInternal(
+            @NonNull String key, PinnedStatus requestedPinnedStatus) {
         HeadsUpEntry headsUpEntry = mHeadsUpEntryMap.get(key);
-        mLogger.logUpdateNotification(key, shouldHeadsUpAgain, headsUpEntry != null);
+        mLogger.logUpdateNotification(key, requestedPinnedStatus, headsUpEntry != null);
         if (headsUpEntry == null) {
             // the entry was released before this update (i.e by a listener) This can happen
             // with the groupmanager
@@ -404,14 +403,11 @@
             headsUpEntry.mEntry.sendAccessibilityEvent(
                     AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
         }
-        if (shouldHeadsUpAgain) {
+        if (requestedPinnedStatus.isPinned()) {
             headsUpEntry.updateEntry(true /* updatePostTime */, "updateNotification");
-            PinnedStatus pinnedStatus = shouldHeadsUpBecomePinned(headsUpEntry.mEntry)
-                    ? PinnedStatus.PinnedBySystem
-                    : PinnedStatus.NotPinned;
-            if (headsUpEntry != null) {
-                setEntryPinned(headsUpEntry, pinnedStatus, "updateNotificationInternal");
-            }
+            PinnedStatus pinnedStatus =
+                    getNewPinnedStatusForEntry(headsUpEntry, requestedPinnedStatus);
+            setEntryPinned(headsUpEntry, pinnedStatus, "updateNotificationInternal");
         }
     }
 
@@ -517,8 +513,7 @@
     }
 
     /**
-     * @param key
-     * @return When a HUN entry should be removed in milliseconds from now
+     * @return When a HUN entry with the given key should be removed in milliseconds from now
      */
     @Override
     public long getEarliestRemovalTime(String key) {
@@ -530,7 +525,10 @@
     }
 
     @VisibleForTesting
-    protected boolean shouldHeadsUpBecomePinned(@NonNull NotificationEntry entry) {
+    boolean shouldHeadsUpBecomePinned(@Nullable NotificationEntry entry) {
+        if (entry == null) {
+            return false;
+        }
         boolean pin = mStatusBarState == StatusBarState.SHADE && !mIsShadeOrQsExpanded;
         if (SceneContainerFlag.isEnabled()) {
             pin |= mIsQsExpanded;
@@ -551,24 +549,18 @@
         return hasFullScreenIntent(entry) && !headsUpEntry.mWasUnpinned;
     }
 
-    protected boolean hasFullScreenIntent(@NonNull NotificationEntry entry) {
-        if (entry == null) {
-            return false;
-        }
-        if (entry.getSbn() == null) {
-            return false;
-        }
+    private boolean hasFullScreenIntent(@NonNull NotificationEntry entry) {
         if (entry.getSbn().getNotification() == null) {
             return false;
         }
         return entry.getSbn().getNotification().fullScreenIntent != null;
     }
 
-    protected void setEntryPinned(
+    private void setEntryPinned(
             @NonNull HeadsUpManagerImpl.HeadsUpEntry headsUpEntry, PinnedStatus pinnedStatus,
             String reason) {
-        mLogger.logSetEntryPinned(headsUpEntry.mEntry, pinnedStatus, reason);
-        NotificationEntry entry = headsUpEntry.mEntry;
+        NotificationEntry entry = headsUpEntry.requireEntry();
+        mLogger.logSetEntryPinned(entry, pinnedStatus, reason);
         boolean isPinned = pinnedStatus.isPinned();
         if (!isPinned) {
             headsUpEntry.mWasUnpinned = true;
@@ -576,7 +568,7 @@
         if (headsUpEntry.getPinnedStatus().getValue() != pinnedStatus) {
             headsUpEntry.setRowPinnedStatus(pinnedStatus);
             updatePinnedMode();
-            if (isPinned && entry.getSbn() != null) {
+            if (isPinned) {
                mUiEventLogger.logWithInstanceId(
                         NotificationPeekEvent.NOTIFICATION_PEEK, entry.getSbn().getUid(),
                         entry.getSbn().getPackageName(), entry.getSbn().getInstanceId());
@@ -596,27 +588,48 @@
      * Manager-specific logic that should occur when an entry is added.
      * @param headsUpEntry entry added
      */
-    protected void onEntryAdded(HeadsUpEntry headsUpEntry) {
-        NotificationEntry entry = headsUpEntry.mEntry;
+    @VisibleForTesting
+     void onEntryAdded(HeadsUpEntry headsUpEntry, PinnedStatus requestedPinnedStatus) {
+        NotificationEntry entry = headsUpEntry.requireEntry();
         entry.setHeadsUp(true);
 
-        final PinnedStatus pinnedStatus = shouldHeadsUpBecomePinned(entry)
-                ? PinnedStatus.PinnedBySystem
-                : PinnedStatus.NotPinned;
+        PinnedStatus pinnedStatus = getNewPinnedStatusForEntry(headsUpEntry, requestedPinnedStatus);
         setEntryPinned(headsUpEntry, pinnedStatus, "onEntryAdded");
         EventLogTags.writeSysuiHeadsUpStatus(entry.getKey(), 1 /* visible */);
         for (OnHeadsUpChangedListener listener : mListeners) {
+            // TODO(b/382509804): It's odd that if pinnedStatus == PinnedStatus.NotPinned, then we
+            //  still send isHeadsUp=true to listeners. Is this causing bugs?
             listener.onHeadsUpStateChanged(entry, true);
         }
         updateTopHeadsUpFlow();
         updateHeadsUpFlow();
     }
 
+    private PinnedStatus getNewPinnedStatusForEntry(
+            HeadsUpEntry headsUpEntry, PinnedStatus requestedPinnedStatus) {
+        NotificationEntry entry = headsUpEntry.mEntry;
+        if (entry == null) {
+            return PinnedStatus.NotPinned;
+        }
+        boolean shouldBecomePinned = shouldHeadsUpBecomePinned(entry);
+        if (!shouldBecomePinned) {
+            return PinnedStatus.NotPinned;
+        }
+
+        if (!StatusBarNotifChips.isEnabled()
+                && requestedPinnedStatus == PinnedStatus.PinnedByUser) {
+            Log.wtf(TAG, "PinnedStatus.PinnedByUser not allowed if StatusBarNotifChips flag off");
+            return PinnedStatus.NotPinned;
+        }
+
+        return requestedPinnedStatus;
+    }
+
     /**
      * Remove a notification from the alerting entries.
      * @param key key of notification to remove
      */
-    protected final void removeEntry(@NonNull String key, String reason) {
+    private void removeEntry(@NonNull String key, String reason) {
         HeadsUpEntry headsUpEntry = mHeadsUpEntryMap.get(key);
         boolean isWaiting;
         if (headsUpEntry == null) {
@@ -633,10 +646,10 @@
             if (finalHeadsUpEntry == null) {
                 return;
             }
-            NotificationEntry entry = finalHeadsUpEntry.mEntry;
+            NotificationEntry entry = finalHeadsUpEntry.requireEntry();
 
             // If the notification is animating, we will remove it at the end of the animation.
-            if (entry != null && entry.isExpandAnimationRunning()) {
+            if (entry.isExpandAnimationRunning()) {
                 return;
             }
             entry.demoteStickyHun();
@@ -658,8 +671,9 @@
      * @param headsUpEntry entry removed
      * @param reason why onEntryRemoved was called
      */
-    protected void onEntryRemoved(HeadsUpEntry headsUpEntry, String reason) {
-        NotificationEntry entry = headsUpEntry.mEntry;
+    @VisibleForTesting
+    void onEntryRemoved(@NonNull HeadsUpEntry headsUpEntry, String reason) {
+        NotificationEntry entry = headsUpEntry.requireEntry();
         entry.setHeadsUp(false);
         setEntryPinned(headsUpEntry, PinnedStatus.NotPinned, "onEntryRemoved");
         EventLogTags.writeSysuiHeadsUpStatus(entry.getKey(), 0 /* visible */);
@@ -681,15 +695,14 @@
             // mEntriesToRemoveWhenReorderingAllowed, we should not remove from this list (and cause
             // ArrayIndexOutOfBoundsException). We don't need to in this case anyway, because we
             // clear mEntriesToRemoveWhenReorderingAllowed after removing these entries.
-            if (!reason.equals(REASON_REORDER_ALLOWED)
-                    && mEntriesToRemoveWhenReorderingAllowed.contains(notifEntry)) {
+            if (!reason.equals(REASON_REORDER_ALLOWED)) {
                 mEntriesToRemoveWhenReorderingAllowed.remove(notifEntry);
             }
         }
     }
 
     private void updateTopHeadsUpFlow() {
-        mTopHeadsUpRow.setValue((HeadsUpRowRepository) getTopHeadsUpEntry());
+        mTopHeadsUpRow.setValue(getTopHeadsUpEntry());
     }
 
     private void updateHeadsUpFlow() {
@@ -734,23 +747,13 @@
         }
     }
 
-    /**
-     * Manager-specific logic, that should occur, when the entry is updated, and its posted time has
-     * changed.
-     *
-     * @param headsUpEntry entry updated
-     */
-    protected void onEntryUpdated(HeadsUpEntry headsUpEntry) {
-        // no need to update the list here
-        updateTopHeadsUpFlow();
-    }
-
-    protected void updatePinnedMode() {
+    private void updatePinnedMode() {
         boolean hasPinnedNotification = hasPinnedNotificationInternal();
+        mPinnedNotificationStatus = pinnedNotificationStatusInternal();
         if (hasPinnedNotification == mHasPinnedNotification) {
             return;
         }
-        mLogger.logUpdatePinnedMode(hasPinnedNotification);
+        mLogger.logUpdatePinnedMode(hasPinnedNotification, mPinnedNotificationStatus);
         mHasPinnedNotification = hasPinnedNotification;
         if (mHasPinnedNotification) {
             MetricsLogger.count(mContext, "note_peek", 1);
@@ -786,7 +789,7 @@
         keySet.addAll(mAvalancheController.getWaitingKeys());
         for (String key : keySet) {
             HeadsUpEntry entry = getHeadsUpEntry(key);
-            if (entry.mEntry == null) {
+            if (entry == null || entry.mEntry == null) {
                 continue;
             }
             String packageName = entry.mEntry.getSbn().getPackageName();
@@ -808,7 +811,8 @@
     }
 
     @Nullable
-    protected HeadsUpEntry getHeadsUpEntry(@NonNull String key) {
+    @VisibleForTesting
+    HeadsUpEntry getHeadsUpEntry(@NonNull String key) {
         if (mHeadsUpEntryMap.containsKey(key)) {
             return mHeadsUpEntryMap.get(key);
         }
@@ -825,7 +829,7 @@
     }
 
     @Nullable
-    protected HeadsUpEntry getTopHeadsUpEntry() {
+    private HeadsUpEntry getTopHeadsUpEntry() {
         if (mHeadsUpEntryMap.isEmpty()) {
             return null;
         }
@@ -921,7 +925,7 @@
         dumpInternal(pw, args);
     }
 
-    protected void dumpInternal(@NonNull PrintWriter pw, @NonNull String[] args) {
+    private void dumpInternal(@NonNull PrintWriter pw, @NonNull String[] args) {
         pw.print("  mTouchAcceptanceDelay="); pw.println(mTouchAcceptanceDelay);
         pw.print("  mSnoozeLengthMs="); pw.println(mSnoozeLengthMs);
         pw.print("  now="); pw.println(mSystemClock.elapsedRealtime());
@@ -941,23 +945,40 @@
         pw.println(mTouchableRegion);
     }
 
-    /**
-     * Returns if there are any pinned Heads Up Notifications or not.
-     */
+    @Override
     public boolean hasPinnedHeadsUp() {
         return mHasPinnedNotification;
     }
 
+    @Override
+    @NonNull
+    public PinnedStatus pinnedHeadsUpStatus() {
+        if (!StatusBarNotifChips.isEnabled()) {
+            return mHasPinnedNotification ? PinnedStatus.PinnedBySystem : PinnedStatus.NotPinned;
+        }
+        return mPinnedNotificationStatus;
+    }
+
     private boolean hasPinnedNotificationInternal() {
         for (String key : mHeadsUpEntryMap.keySet()) {
             HeadsUpEntry entry = getHeadsUpEntry(key);
-            if (entry.mEntry != null && entry.mEntry.isRowPinned()) {
+            if (entry != null && entry.mEntry != null && entry.mEntry.isRowPinned()) {
                 return true;
             }
         }
         return false;
     }
 
+    private PinnedStatus pinnedNotificationStatusInternal() {
+        for (String key : mHeadsUpEntryMap.keySet()) {
+            HeadsUpEntry entry = getHeadsUpEntry(key);
+            if (entry.mEntry != null && entry.mEntry.isRowPinned()) {
+                return entry.mEntry.getPinnedStatus();
+            }
+        }
+        return PinnedStatus.NotPinned;
+    }
+
     /**
      * Unpins all pinned Heads Up Notifications.
      * @param userUnPinned The unpinned action is trigger by user real operation.
@@ -966,6 +987,10 @@
     public void unpinAll(boolean userUnPinned) {
         for (String key : mHeadsUpEntryMap.keySet()) {
             HeadsUpEntry headsUpEntry = getHeadsUpEntry(key);
+            if (headsUpEntry == null) {
+                Log.wtf(TAG, "Couldn't find entry " + key + " in unpinAll");
+                continue;
+            }
             mLogger.logUnpinEntryRequest(key);
             Runnable runnable = () -> {
                 mLogger.logUnpinEntry(key);
@@ -976,10 +1001,10 @@
 
                 // when the user unpinned all of HUNs by moving one HUN, all of HUNs should not stay
                 // on the screen.
-                if (userUnPinned && headsUpEntry.mEntry != null) {
-                    if (headsUpEntry.mEntry != null && headsUpEntry.mEntry.mustStayOnScreen()) {
-                        headsUpEntry.mEntry.setHeadsUpIsVisible();
-                    }
+                if (userUnPinned
+                        && headsUpEntry.mEntry != null
+                        && headsUpEntry.mEntry.mustStayOnScreen()) {
+                    headsUpEntry.mEntry.setHeadsUpIsVisible();
                 }
             };
             mAvalancheController.delete(headsUpEntry, runnable, "unpinAll");
@@ -1000,7 +1025,7 @@
             } else {
                 headsUpEntry.updateEntry(false /* updatePostTime */, "setRemoteInputActive(false)");
             }
-            onEntryUpdated(headsUpEntry);
+            updateTopHeadsUpFlow();
         }
     }
 
@@ -1076,7 +1101,7 @@
      *
      * @param entry the entry that might be indirectly removed by the user's action
      *
-     * @see HeadsUpCoordinator#mActionPressListener
+     * @see HeadsUpCoordinator.mActionPressListener
      * @see #canRemoveImmediately(String)
      */
     public void setUserActionMayIndirectlyRemove(@NonNull NotificationEntry entry) {
@@ -1115,8 +1140,8 @@
     }
 
     /**
-     * @param key
-     * @return true if the entry is (pinned and expanded) or (has an active remote input)
+     * @return true if the entry with the given key is (pinned and expanded) or (has an active
+     * remote input)
      */
     @Override
     public boolean isSticky(String key) {
@@ -1128,7 +1153,8 @@
     }
 
     @NonNull
-    protected HeadsUpEntry createHeadsUpEntry(NotificationEntry entry) {
+    @VisibleForTesting
+    HeadsUpEntry createHeadsUpEntry(NotificationEntry entry) {
         if (NotificationThrottleHun.isEnabled()) {
             return new HeadsUpEntry(entry);
         } else {
@@ -1152,7 +1178,7 @@
     }
 
     @VisibleForTesting
-    public final OnReorderingAllowedListener mOnReorderingAllowedListener = () -> {
+    final OnReorderingAllowedListener mOnReorderingAllowedListener = () -> {
         if (NotificationThrottleHun.isEnabled()) {
             mAvalancheController.setEnableAtRuntime(true);
             if (mEntriesToRemoveWhenReorderingAllowed.isEmpty()) {
@@ -1229,14 +1255,15 @@
         public boolean mRemoteInputActive;
         public boolean mUserActionMayIndirectlyRemove;
 
-        protected boolean mExpanded;
-        protected boolean mWasUnpinned;
+        private boolean mExpanded;
+        @VisibleForTesting
+        boolean mWasUnpinned;
 
         @Nullable public NotificationEntry mEntry;
         public long mPostTime;
         public long mEarliestRemovalTime;
 
-        @Nullable protected Runnable mRemoveRunnable;
+        @Nullable private Runnable mRemoveRunnable;
 
         @Nullable private Runnable mCancelRemoveRunnable;
 
@@ -1288,7 +1315,7 @@
             setEntry(entry, createRemoveRunnable(entry));
         }
 
-        protected void setEntry(
+        private void setEntry(
                 @NonNull final NotificationEntry entry,
                 @Nullable Runnable removeRunnable) {
             mEntry = entry;
@@ -1305,11 +1332,8 @@
             }
         }
 
-        protected boolean isRowPinned() {
-            return mEntry != null && mEntry.isRowPinned();
-        }
-
-        protected void setRowPinnedStatus(PinnedStatus pinnedStatus) {
+        @VisibleForTesting
+        void setRowPinnedStatus(PinnedStatus pinnedStatus) {
             if (mEntry != null) mEntry.setRowPinnedStatus(pinnedStatus);
             mPinnedStatus.setValue(pinnedStatus);
         }
@@ -1317,7 +1341,7 @@
         /**
          * An interface that returns the amount of time left this HUN should show.
          */
-        interface FinishTimeUpdater {
+        private interface FinishTimeUpdater {
             long updateAndGetTimeRemaining();
         }
 
@@ -1337,6 +1361,10 @@
         public void updateEntry(boolean updatePostTime, boolean updateEarliestRemovalTime,
                 @Nullable String reason) {
             Runnable runnable = () -> {
+                if (mEntry == null) {
+                    Log.wtf(TAG, "#updateEntry called with null mEntry; returning early");
+                    return;
+                }
                 mLogger.logUpdateEntry(mEntry, updatePostTime, reason);
 
                 final long now = mSystemClock.elapsedRealtime();
@@ -1358,23 +1386,18 @@
             FinishTimeUpdater finishTimeCalculator = () -> {
                 final long finishTime = calculateFinishTime();
                 final long now = mSystemClock.elapsedRealtime();
-                final long timeLeft = NotificationThrottleHun.isEnabled()
+                return NotificationThrottleHun.isEnabled()
                         ? Math.max(finishTime, mEarliestRemovalTime) - now
                         : Math.max(finishTime - now, mMinimumDisplayTime);
-                return timeLeft;
             };
             scheduleAutoRemovalCallback(finishTimeCalculator, "updateEntry (not sticky)");
 
             // Notify the manager, that the posted time has changed.
-            onEntryUpdated(this);
+            updateTopHeadsUpFlow();
 
-            if (mEntriesToRemoveAfterExpand.contains(mEntry)) {
-                mEntriesToRemoveAfterExpand.remove(mEntry);
-            }
+            mEntriesToRemoveAfterExpand.remove(mEntry);
             if (!NotificationThrottleHun.isEnabled()) {
-                if (mEntriesToRemoveWhenReorderingAllowed.contains(mEntry)) {
-                    mEntriesToRemoveWhenReorderingAllowed.remove(mEntry);
-                }
+                mEntriesToRemoveWhenReorderingAllowed.remove(mEntry);
             }
         }
 
@@ -1495,8 +1518,7 @@
         @Override
         public boolean equals(@Nullable Object o) {
             if (this == o) return true;
-            if (o == null || !(o instanceof HeadsUpEntry)) return false;
-            HeadsUpEntry otherHeadsUpEntry = (HeadsUpEntry) o;
+            if (!(o instanceof HeadsUpEntry otherHeadsUpEntry)) return false;
             if (mEntry != null && otherHeadsUpEntry.mEntry != null) {
                 return mEntry.getKey().equals(otherHeadsUpEntry.mEntry.getKey());
             }
@@ -1560,10 +1582,13 @@
             }
         }
 
-        public void scheduleAutoRemovalCallback(FinishTimeUpdater finishTimeCalculator,
+        private void scheduleAutoRemovalCallback(FinishTimeUpdater finishTimeCalculator,
                 @NonNull String reason) {
-
-            mLogger.logAutoRemoveRequest(this.mEntry, reason);
+            if (mEntry == null) {
+                Log.wtf(TAG, "#scheduleAutoRemovalCallback with null mEntry; returning early");
+                return;
+            }
+            mLogger.logAutoRemoveRequest(mEntry, reason);
             Runnable runnable = () -> {
                 long delayMs = finishTimeCalculator.updateAndGetTimeRemaining();
 
@@ -1603,16 +1628,14 @@
         public void removeAsSoonAsPossible() {
             if (mRemoveRunnable != null) {
 
-                FinishTimeUpdater finishTimeCalculator = () -> {
-                    final long timeLeft = mEarliestRemovalTime - mSystemClock.elapsedRealtime();
-                    return timeLeft;
-                };
+                FinishTimeUpdater finishTimeCalculator = () ->
+                        mEarliestRemovalTime - mSystemClock.elapsedRealtime();
                 scheduleAutoRemovalCallback(finishTimeCalculator, "removeAsSoonAsPossible");
             }
         }
 
         /** Creates a runnable to remove this notification from the alerting entries. */
-        protected Runnable createRemoveRunnable(NotificationEntry entry) {
+        private Runnable createRemoveRunnable(NotificationEntry entry) {
             return () -> {
                 if (!NotificationThrottleHun.isEnabled()
                         && !mVisualStabilityProvider.isReorderingAllowed()
@@ -1636,7 +1659,7 @@
          * Calculate what the post time of a notification is at some current time.
          * @return the post time
          */
-        protected long calculatePostTime() {
+        private long calculatePostTime() {
             // The actual post time will be just after the heads-up really slided in
             return mSystemClock.elapsedRealtime() + mTouchAcceptanceDelay;
         }
@@ -1645,7 +1668,7 @@
          * @return When the notification should auto-dismiss itself, based on
          * {@link SystemClock#elapsedRealtime()}
          */
-        protected long calculateFinishTime() {
+        private long calculateFinishTime() {
             int requestedTimeOutMs;
             if (isStickyForSomeTime()) {
                 requestedTimeOutMs = mStickyForSomeTimeAutoDismissTime;
@@ -1659,9 +1682,8 @@
         /**
          * Get user-preferred or default timeout duration. The larger one will be returned.
          * @return milliseconds before auto-dismiss
-         * @param requestedTimeout
          */
-        protected int getRecommendedHeadsUpTimeoutMs(int requestedTimeout) {
+        private int getRecommendedHeadsUpTimeoutMs(int requestedTimeout) {
             return mAccessibilityMgr.getRecommendedTimeoutMillis(
                     requestedTimeout,
                     AccessibilityManager.FLAG_CONTENT_CONTROLS
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerLogger.kt
index 80225c4..e3ca7c8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerLogger.kt
@@ -44,8 +44,16 @@
         buffer.log(TAG, INFO, {}, { "release all immediately" })
     }
 
-    fun logShowNotificationRequest(entry: NotificationEntry) {
-        buffer.log(TAG, INFO, { str1 = entry.logKey }, { "request: show notification $str1" })
+    fun logShowNotificationRequest(entry: NotificationEntry, isPinnedByUser: Boolean) {
+        buffer.log(
+            TAG,
+            INFO,
+            {
+                str1 = entry.logKey
+                bool1 = isPinnedByUser
+            },
+            { "request: show notification $str1. isPinnedByUser=$bool1" },
+        )
     }
 
     fun logAvalancheUpdate(
@@ -86,8 +94,16 @@
         )
     }
 
-    fun logShowNotification(entry: NotificationEntry) {
-        buffer.log(TAG, INFO, { str1 = entry.logKey }, { "show notification $str1" })
+    fun logShowNotification(entry: NotificationEntry, isPinnedByUser: Boolean) {
+        buffer.log(
+            TAG,
+            INFO,
+            {
+                str1 = entry.logKey
+                bool1 = isPinnedByUser
+            },
+            { "show notification $str1. isPinnedByUser=$bool1" },
+        )
     }
 
     fun logAutoRemoveScheduled(entry: NotificationEntry, delayMillis: Long, reason: String) {
@@ -140,12 +156,12 @@
         )
     }
 
-    fun logAutoRemoveCanceled(entry: NotificationEntry, reason: String?) {
+    fun logAutoRemoveCanceled(entry: NotificationEntry?, reason: String?) {
         buffer.log(
             TAG,
             INFO,
             {
-                str1 = entry.logKey
+                str1 = entry?.logKey
                 str2 = reason ?: "unknown"
             },
             { "cancel auto remove of $str1 reason: $str2" },
@@ -224,29 +240,33 @@
         buffer.log(TAG, INFO, { str1 = entry.logKey }, { "notification removed $str1 " })
     }
 
-    fun logUpdateNotificationRequest(key: String, alert: Boolean, hasEntry: Boolean) {
+    fun logUpdateNotificationRequest(
+        key: String,
+        requestedPinnedStatus: PinnedStatus,
+        hasEntry: Boolean,
+    ) {
         buffer.log(
             TAG,
             INFO,
             {
                 str1 = logKey(key)
-                bool1 = alert
-                bool2 = hasEntry
+                bool1 = hasEntry
+                str2 = requestedPinnedStatus.name
             },
-            { "request: update notification $str1 alert: $bool1 hasEntry: $bool2" },
+            { "request: update notification $str1. hasEntry: $bool1. requestedPinnedStatus: $str2" },
         )
     }
 
-    fun logUpdateNotification(key: String, alert: Boolean, hasEntry: Boolean) {
+    fun logUpdateNotification(key: String, requestedPinnedStatus: PinnedStatus, hasEntry: Boolean) {
         buffer.log(
             TAG,
             INFO,
             {
                 str1 = logKey(key)
-                bool1 = alert
-                bool2 = hasEntry
+                bool1 = hasEntry
+                str2 = requestedPinnedStatus.name
             },
-            { "update notification $str1 alert: $bool1 hasEntry: $bool2" },
+            { "update notification $str1. hasEntry: $bool2. requestedPinnedStatus: $str2" },
         )
     }
 
@@ -285,12 +305,18 @@
         )
     }
 
-    fun logUpdatePinnedMode(hasPinnedNotification: Boolean) {
+    fun logUpdatePinnedMode(
+        hasPinnedNotification: Boolean,
+        pinnedNotificationStatus: PinnedStatus,
+    ) {
         buffer.log(
             TAG,
             INFO,
-            { bool1 = hasPinnedNotification },
-            { "has pinned notification changed to $bool1" },
+            {
+                bool1 = hasPinnedNotification
+                str1 = pinnedNotificationStatus.name
+            },
+            { "has pinned notification changed to $bool1, status=$str1" },
         )
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerAlwaysOnDisplayViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerAlwaysOnDisplayViewBinder.kt
index fc432ba..839028e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerAlwaysOnDisplayViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerAlwaysOnDisplayViewBinder.kt
@@ -16,62 +16,9 @@
 
 package com.android.systemui.statusbar.notification.icon.ui.viewbinder
 
-import androidx.lifecycle.lifecycleScope
-import com.android.app.tracing.coroutines.launchTraced as launch
-import com.android.app.tracing.traceSection
-import com.android.systemui.common.ui.ConfigurationState
-import com.android.systemui.keyguard.ui.binder.KeyguardRootViewBinder
-import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
-import com.android.systemui.lifecycle.repeatWhenAttached
-import com.android.systemui.shade.ShadeDisplayAware
 import com.android.systemui.statusbar.notification.collection.NotifCollection
 import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerViewBinder.IconViewStore
-import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerAlwaysOnDisplayViewModel
-import com.android.systemui.statusbar.phone.NotificationIconContainer
-import com.android.systemui.statusbar.phone.ScreenOffAnimationController
-import com.android.systemui.statusbar.ui.SystemBarUtilsState
 import javax.inject.Inject
-import kotlinx.coroutines.DisposableHandle
-
-/** Binds a [NotificationIconContainer] to a [NotificationIconContainerAlwaysOnDisplayViewModel]. */
-class NotificationIconContainerAlwaysOnDisplayViewBinder
-@Inject
-constructor(
-    private val viewModel: NotificationIconContainerAlwaysOnDisplayViewModel,
-    private val keyguardRootViewModel: KeyguardRootViewModel,
-    @ShadeDisplayAware private val configuration: ConfigurationState,
-    private val failureTracker: StatusBarIconViewBindingFailureTracker,
-    private val screenOffAnimationController: ScreenOffAnimationController,
-    private val systemBarUtilsState: SystemBarUtilsState,
-    private val viewStore: AlwaysOnDisplayNotificationIconViewStore,
-) {
-    fun bindWhileAttached(view: NotificationIconContainer): DisposableHandle {
-        return traceSection("NICAlwaysOnDisplay#bindWhileAttached") {
-            view.repeatWhenAttached {
-                lifecycleScope.launch {
-                    launch {
-                        NotificationIconContainerViewBinder.bind(
-                            view = view,
-                            viewModel = viewModel,
-                            configuration = configuration,
-                            systemBarUtilsState = systemBarUtilsState,
-                            failureTracker = failureTracker,
-                            viewStore = viewStore,
-                        )
-                    }
-                    launch {
-                        KeyguardRootViewBinder.bindAodNotifIconVisibility(
-                            view = view,
-                            isVisible = keyguardRootViewModel.isNotifIconContainerVisible,
-                            configuration = configuration,
-                            screenOffAnimationController = screenOffAnimationController,
-                        )
-                    }
-                }
-            }
-        }
-    }
-}
 
 /** [IconViewStore] for the always-on display. */
 class AlwaysOnDisplayNotificationIconViewStore
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinder.java
index 9a7610d..32ec023 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinder.java
@@ -28,7 +28,6 @@
 import com.android.systemui.statusbar.NotificationPresenter;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.coordinator.HeadsUpCoordinator;
-import com.android.systemui.statusbar.notification.row.NotifBindPipeline.BindCallback;
 import com.android.systemui.statusbar.notification.row.RowContentBindParams;
 import com.android.systemui.statusbar.notification.row.RowContentBindStage;
 
@@ -73,7 +72,10 @@
      * Bind heads up view to the notification row.
      * @param callback callback after heads up view is bound
      */
-    public void bindHeadsUpView(NotificationEntry entry, @Nullable BindCallback callback) {
+    public void bindHeadsUpView(
+            NotificationEntry entry,
+            boolean isPinnedByUser,
+            @Nullable HeadsUpBindCallback callback) {
         RowContentBindParams params = mStage.getStageParams(entry);
         final boolean isImportantMessage = mNotificationMessagingUtil.isImportantMessaging(
                 entry.getSbn(), entry.getImportance());
@@ -84,16 +86,16 @@
         CancellationSignal signal = mStage.requestRebind(entry, en -> {
             mLogger.entryBoundSuccessfully(entry);
             en.getRow().setUsesIncreasedHeadsUpHeight(params.useIncreasedHeadsUpHeight());
-            // requestRebing promises that if we called cancel before this callback would be
+            // requestRebind promises that if we called cancel before this callback would be
             // invoked, then we will not enter this callback, and because we always cancel before
             // adding to this map, we know this will remove the correct signal.
             mOngoingBindCallbacks.remove(entry);
             if (callback != null) {
-                callback.onBindFinished(en);
+                callback.onHeadsUpBindFinished(en, isPinnedByUser);
             }
         });
         abortBindCallback(entry);
-        mLogger.startBindingHun(entry);
+        mLogger.startBindingHun(entry, isPinnedByUser);
         mOngoingBindCallbacks.put(entry, signal);
     }
 
@@ -129,4 +131,14 @@
         mLogger.entryContentViewMarkedFreeable(entry);
         mStage.requestRebind(entry, e -> mLogger.entryUnbound(e));
     }
+
+    /**
+     * Interface for bind callback.
+     */
+    public interface HeadsUpBindCallback {
+        /**
+         * Called when all views are fully bound on the notification.
+         */
+        void onHeadsUpBindFinished(NotificationEntry entry, boolean isPinnedByUser);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinderLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinderLogger.kt
index c6d2861..e690fa5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinderLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinderLogger.kt
@@ -1,59 +1,63 @@
 package com.android.systemui.statusbar.notification.interruption
 
-import com.android.systemui.log.dagger.NotificationHeadsUpLog
 import com.android.systemui.log.LogBuffer
 import com.android.systemui.log.core.LogLevel.INFO
+import com.android.systemui.log.dagger.NotificationHeadsUpLog
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
 import com.android.systemui.statusbar.notification.logKey
 import javax.inject.Inject
 
 class HeadsUpViewBinderLogger @Inject constructor(@NotificationHeadsUpLog val buffer: LogBuffer) {
-    fun startBindingHun(entry: NotificationEntry) {
-        buffer.log(TAG, INFO, {
-            str1 = entry.logKey
-        }, {
-            "start binding heads up entry $str1 "
-        })
+    fun startBindingHun(entry: NotificationEntry, isPinnedByUser: Boolean) {
+        buffer.log(
+            TAG,
+            INFO,
+            {
+                str1 = entry.logKey
+                bool1 = isPinnedByUser
+            },
+            { "start binding heads up entry $str1. isPinnedByUser=$bool1 " },
+        )
     }
 
     fun currentOngoingBindingAborted(entry: NotificationEntry) {
-        buffer.log(TAG, INFO, {
-            str1 = entry.logKey
-        }, {
-            "aborted potential ongoing heads up entry binding $str1 "
-        })
+        buffer.log(
+            TAG,
+            INFO,
+            { str1 = entry.logKey },
+            { "aborted potential ongoing heads up entry binding $str1 " },
+        )
     }
 
     fun entryBoundSuccessfully(entry: NotificationEntry) {
-        buffer.log(TAG, INFO, {
-            str1 = entry.logKey
-        }, {
-            "heads up entry bound successfully $str1 "
-        })
+        buffer.log(
+            TAG,
+            INFO,
+            { str1 = entry.logKey },
+            { "heads up entry bound successfully $str1 " },
+        )
     }
 
     fun entryUnbound(entry: NotificationEntry) {
-        buffer.log(TAG, INFO, {
-            str1 = entry.logKey
-        }, {
-            "heads up entry unbound successfully $str1 "
-        })
+        buffer.log(
+            TAG,
+            INFO,
+            { str1 = entry.logKey },
+            { "heads up entry unbound successfully $str1 " },
+        )
     }
 
     fun entryContentViewMarkedFreeable(entry: NotificationEntry) {
-        buffer.log(TAG, INFO, {
-            str1 = entry.logKey
-        }, {
-            "start unbinding heads up entry $str1 "
-        })
+        buffer.log(TAG, INFO, { str1 = entry.logKey }, { "start unbinding heads up entry $str1 " })
     }
 
     fun entryBindStageParamsNullOnUnbind(entry: NotificationEntry) {
-        buffer.log(TAG, INFO, {
-            str1 = entry.logKey
-        }, {
-            "heads up entry bind stage params null on unbind $str1 "
-        })
+        buffer.log(
+            TAG,
+            INFO,
+            { str1 = entry.logKey },
+            { "heads up entry bind stage params null on unbind $str1 " },
+        )
     }
 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractor.kt
index e122ca8..863c665 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractor.kt
@@ -71,6 +71,7 @@
         contentBuilder.appName = notification.loadHeaderAppName(context)
         contentBuilder.subText = notification.subText()
         contentBuilder.time = notification.extractWhen()
+        contentBuilder.shortCriticalText = notification.shortCriticalText()
         contentBuilder.lastAudiblyAlertedMs = entry.lastAudiblyAlertedMs
         contentBuilder.profileBadgeResId = null // TODO
         contentBuilder.title = notification.title()
@@ -97,6 +98,13 @@
 
 private fun Notification.subText(): String? = extras?.getString(EXTRA_SUB_TEXT)
 
+private fun Notification.shortCriticalText(): String? {
+    if (!android.app.Flags.apiRichOngoing()) {
+        return null
+    }
+    return this.shortCriticalText
+}
+
 private fun Notification.chronometerCountDown(): Boolean =
     extras?.getBoolean(EXTRA_CHRONOMETER_COUNT_DOWN, /* defaultValue= */ false) ?: false
 
@@ -107,7 +115,7 @@
     val countDown = chronometerCountDown()
 
     return when {
-        showsTime -> When(time, When.Mode.Absolute)
+        showsTime -> When(time, When.Mode.BasicTime)
         showsChronometer -> When(time, if (countDown) When.Mode.CountDown else When.Mode.CountUp)
         else -> null
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/shared/model/PromotedNotificationContentModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/shared/model/PromotedNotificationContentModel.kt
index 0af4043..fe2dabe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/shared/model/PromotedNotificationContentModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/shared/model/PromotedNotificationContentModel.kt
@@ -34,6 +34,11 @@
     val skeletonSmallIcon: Icon?, // TODO(b/377568176): Make into an IconModel.
     val appName: CharSequence?,
     val subText: CharSequence?,
+    val shortCriticalText: String?,
+    /**
+     * The timestamp associated with the notification. Null if the timestamp should not be
+     * displayed.
+     */
     val time: When?,
     val lastAudiblyAlertedMs: Long,
     @DrawableRes val profileBadgeResId: Int?,
@@ -57,6 +62,7 @@
         var appName: CharSequence? = null
         var subText: CharSequence? = null
         var time: When? = null
+        var shortCriticalText: String? = null
         var lastAudiblyAlertedMs: Long = 0L
         @DrawableRes var profileBadgeResId: Int? = null
         var title: CharSequence? = null
@@ -80,6 +86,7 @@
                 skeletonSmallIcon = skeletonSmallIcon,
                 appName = appName,
                 subText = subText,
+                shortCriticalText = shortCriticalText,
                 time = time,
                 lastAudiblyAlertedMs = lastAudiblyAlertedMs,
                 profileBadgeResId = profileBadgeResId,
@@ -100,8 +107,11 @@
     data class When(val time: Long, val mode: Mode) {
         /** The mode used to display a notification's `when` value. */
         enum class Mode {
-            Absolute,
+            /** No custom mode requested by the notification. */
+            BasicTime,
+            /** Show the notification's time as a chronometer that counts down to [time]. */
             CountDown,
+            /** Show the notification's time as a chronometer that counts up from [time]. */
             CountUp,
         }
     }
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 c8811fd..5a52c37 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
@@ -1247,6 +1247,11 @@
     }
 
     @Override
+    public PinnedStatus getPinnedStatus() {
+        return mPinnedStatus;
+    }
+
+    @Override
     public int getPinnedHeadsUpHeight() {
         return getPinnedHeadsUpHeight(true /* atLeastMinHeight */);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
index ef6cad1..f83a1d9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
@@ -40,6 +40,7 @@
 import com.android.systemui.statusbar.StatusBarIconView;
 import com.android.systemui.statusbar.notification.Roundable;
 import com.android.systemui.statusbar.notification.RoundableState;
+import com.android.systemui.statusbar.notification.headsup.PinnedStatus;
 import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
 import com.android.systemui.util.Compile;
@@ -201,6 +202,11 @@
         return false;
     }
 
+    @NonNull
+    public PinnedStatus getPinnedStatus() {
+        return PinnedStatus.NotPinned;
+    }
+
     public boolean isHeadsUpAnimatingAway() {
         return false;
     }
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 0720899..7c9d850 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
@@ -1284,7 +1284,11 @@
     @Override
     public void setStackCutoff(float stackCutoff) {
         if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return;
-        mAmbientState.setStackCutoff(stackCutoff);
+        if (mAmbientState.getStackCutoff() != stackCutoff) {
+            mAmbientState.setStackCutoff(stackCutoff);
+            updateStackEndHeightAndStackHeight(mAmbientState.getExpansionFraction());
+            requestChildrenUpdate();
+        }
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt
index c5bef99..ef68b4d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt
@@ -109,11 +109,6 @@
                 }
             }
             launch {
-                viewModel.shouldResetStackTop
-                    .filter { it }
-                    .collectTraced { view.setStackTop(-(view.getHeadsUpInset().toFloat())) }
-            }
-            launch {
                 viewModel.shouldCloseGuts
                     .filter { it }
                     .collectTraced { view.closeGutsOnSceneTouch() }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt
index 56b3356..1bb205c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt
@@ -192,12 +192,6 @@
     /** Whether we should close any open notification guts. */
     val shouldCloseGuts: Flow<Boolean> = stackAppearanceInteractor.shouldCloseGuts
 
-    val shouldResetStackTop: Flow<Boolean> =
-        sceneInteractor.transitionState
-            .mapNotNull { state -> state is Idle && state.currentScene == Scenes.Gone }
-            .distinctUntilChanged()
-            .dumpWhileCollecting("shouldResetStackTop")
-
     /** Whether the Notification Stack is visibly on the lockscreen scene. */
     val isShowingStackOnLockscreen: Flow<Boolean> =
         sceneInteractor.transitionState
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 f8f29ff..b81c71e 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
@@ -563,7 +563,7 @@
             lockscreenToGoneTransitionViewModel.notificationAlpha(viewState),
             lockscreenToOccludedTransitionViewModel.lockscreenAlpha,
             lockscreenToPrimaryBouncerTransitionViewModel.lockscreenAlpha,
-            alternateBouncerToPrimaryBouncerTransitionViewModel.lockscreenAlpha,
+            alternateBouncerToPrimaryBouncerTransitionViewModel.notificationAlpha,
             occludedToAodTransitionViewModel.lockscreenAlpha,
             occludedToGoneTransitionViewModel.notificationAlpha(viewState),
             occludedToLockscreenTransitionViewModel.lockscreenAlpha,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ComponentSystemUIDialog.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ComponentSystemUIDialog.kt
index fe5a02b..153dd99 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ComponentSystemUIDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ComponentSystemUIDialog.kt
@@ -67,6 +67,7 @@
         broadcastDispatcher,
         dialogTransitionAnimator,
         delegate,
+        true, /* shouldAcsdDismissDialog */
     ),
     LifecycleOwner,
     SavedStateRegistryOwner,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt
index 858cac1..9c7af18 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt
@@ -65,6 +65,13 @@
         listeners.filterForEach({ this.listeners.contains(it) }) { it.onThemeChanged() }
     }
 
+    override fun dispatchOnMovedToDisplay(newDisplayId: Int, newConfiguration: Configuration) {
+        val listeners = synchronized(this.listeners) { ArrayList(this.listeners) }
+        listeners.filterForEach({ this.listeners.contains(it) }) {
+            it.onMovedToDisplay(newDisplayId, newConfiguration)
+        }
+    }
+
     override fun onConfigurationChanged(newConfig: Configuration) {
         // Avoid concurrent modification exception
         val listeners = synchronized(this.listeners) { ArrayList(this.listeners) }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationForwarder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationForwarder.kt
index 3fd46fc..537e3e1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationForwarder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationForwarder.kt
@@ -28,4 +28,13 @@
 interface ConfigurationForwarder {
     /** Should be called when a new configuration is received. */
     fun onConfigurationChanged(newConfiguration: Configuration)
+
+    /**
+     * Should be called when the view associated to this configuration forwarded moved to another
+     * display, usually as a consequence of [View.onMovedToDisplay].
+     *
+     * For the default configuration forwarder (associated with the global configuration) this is
+     * never expected to be called.
+     */
+    fun dispatchOnMovedToDisplay(newDisplayId: Int, newConfiguration: Configuration)
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
index 6cad68f..53a2950 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
@@ -23,6 +23,7 @@
 import android.view.View;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.widget.ViewClippingUtil;
@@ -36,6 +37,7 @@
 import com.android.systemui.statusbar.CrossFadeHelper;
 import com.android.systemui.statusbar.HeadsUpStatusBarView;
 import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips;
 import com.android.systemui.statusbar.core.StatusBarRootModernization;
 import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
 import com.android.systemui.statusbar.notification.SourceType;
@@ -153,7 +155,7 @@
             @Override
             public void onLayoutChange(View v, int left, int top, int right, int bottom,
                     int oldLeft, int oldTop, int oldRight, int oldBottom) {
-                if (shouldBeVisible()) {
+                if (shouldHeadsUpStatusBarBeVisible()) {
                     updateTopEntry();
 
                     // trigger scroller to notify the latest panel translation
@@ -217,35 +219,54 @@
 
     private void updateTopEntry() {
         NotificationEntry newEntry = null;
-        if (shouldBeVisible()) {
+        if (shouldHeadsUpStatusBarBeVisible()) {
             newEntry = mHeadsUpManager.getTopEntry();
         }
         NotificationEntry previousEntry = mView.getShowingEntry();
         mView.setEntry(newEntry);
         if (newEntry != previousEntry) {
             if (newEntry == null) {
-                // no heads up anymore, lets start the disappear animation
+                // No longer heads up
                 setPinnedStatus(PinnedStatus.NotPinned);
             } else if (previousEntry == null) {
-                // We now have a headsUp and didn't have one before. Let's start the disappear
-                // animation
-                setPinnedStatus(PinnedStatus.PinnedBySystem);
+                // We now have a heads up when we didn't have one before
+                setPinnedStatus(newEntry.getPinnedStatus());
             }
 
-            String isolatedIconKey;
-            if (newEntry != null) {
-                isolatedIconKey = newEntry.getRepresentativeEntry().getKey();
+            mHeadsUpNotificationIconInteractor.setIsolatedIconNotificationKey(
+                    getIsolatedIconKey(newEntry));
+        }
+    }
+
+    private static @Nullable String getIsolatedIconKey(NotificationEntry newEntry) {
+        if (newEntry == null) {
+            return null;
+        }
+        if (StatusBarNotifChips.isEnabled()) {
+            // If the flag is on, only show the isolated icon if the HUN is pinned by the
+            // *system*. (If the HUN was pinned by the user, then the user tapped the
+            // notification status bar chip and we want to keep the chip showing.)
+            if (newEntry.getPinnedStatus() == PinnedStatus.PinnedBySystem) {
+                return newEntry.getRepresentativeEntry().getKey();
             } else {
-                isolatedIconKey = null;
+                return null;
             }
-            mHeadsUpNotificationIconInteractor.setIsolatedIconNotificationKey(isolatedIconKey);
+        } else {
+            // If the flag is off, we know all HUNs are pinned by the system and should show
+            // the isolated icon
+            return newEntry.getRepresentativeEntry().getKey();
         }
     }
 
     private void setPinnedStatus(PinnedStatus pinnedStatus) {
         if (mPinnedStatus != pinnedStatus) {
             mPinnedStatus = pinnedStatus;
-            if (pinnedStatus.isPinned()) {
+
+            boolean shouldShowHunStatusBar = StatusBarNotifChips.isEnabled()
+                    ? mPinnedStatus == PinnedStatus.PinnedBySystem
+                    // If the flag isn't enabled, all HUNs get the normal treatment.
+                    : mPinnedStatus.isPinned();
+            if (shouldShowHunStatusBar) {
                 updateParentClipping(false /* shouldClip */);
                 mView.setVisibility(View.VISIBLE);
                 show(mView);
@@ -333,23 +354,36 @@
         return mPinnedStatus;
     }
 
-    /**
-     * Should the headsup status bar view be visible right now? This may be different from isShown,
-     * since the headsUp manager might not have notified us yet of the state change.
-     *
-     * @return if the heads up status bar view should be shown
-     * @deprecated use HeadsUpNotificationInteractor.showHeadsUpStatusBar instead.
-     */
-    public boolean shouldBeVisible() {
+    /** True if the device's current state allows us to show HUNs and false otherwise. */
+    private boolean canShowHeadsUp() {
         boolean notificationsShown = !mWakeUpCoordinator.getNotificationsFullyHidden();
-        boolean canShow = !isExpanded() && notificationsShown;
         if (mBypassController.getBypassEnabled() &&
                 (mStatusBarStateController.getState() == StatusBarState.KEYGUARD
                         || mKeyguardStateController.isKeyguardGoingAway())
                 && notificationsShown) {
-            canShow = true;
+            return true;
         }
-        return canShow && mHeadsUpManager.hasPinnedHeadsUp();
+        return !isExpanded() && notificationsShown;
+    }
+
+    /**
+     * True if the headsup status bar view (which has just the HUN icon and app name) should be
+     * visible right now and false otherwise.
+     *
+     * @deprecated use {@link com.android.systemui.statusbar.notification.domain.interactor.HeadsUpNotificationInteractor#getStatusBarHeadsUpState()}
+     *    instead.
+     */
+    @Deprecated
+    public boolean shouldHeadsUpStatusBarBeVisible() {
+        if (StatusBarNotifChips.isEnabled()) {
+            return canShowHeadsUp()
+                    && mHeadsUpManager.pinnedHeadsUpStatus() == PinnedStatus.PinnedBySystem;
+            // Note: This means that if mHeadsUpManager.pinnedHeadsUpStatus() == PinnedByUser,
+            // #updateTopEntry won't do anything, so mPinnedStatus will remain as NotPinned and will
+            // *not* update to PinnedByUser.
+        } else {
+            return canShowHeadsUp() && mHeadsUpManager.hasPinnedHeadsUp();
+        }
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.kt
index eadb7f5..2368824 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.kt
@@ -22,14 +22,7 @@
 import android.view.ViewGroup
 import android.widget.FrameLayout
 import androidx.annotation.StringRes
-import com.android.keyguard.LockIconViewController
-import com.android.systemui.keyguard.ui.binder.KeyguardBottomAreaViewBinder
-import com.android.systemui.keyguard.ui.binder.KeyguardBottomAreaViewBinder.bind
-import com.android.systemui.keyguard.ui.viewmodel.KeyguardBottomAreaViewModel
-import com.android.systemui.plugins.ActivityStarter
-import com.android.systemui.plugins.FalsingManager
 import com.android.systemui.res.R
-import com.android.systemui.statusbar.VibratorHelper
 
 /**
  * Renders the bottom area of the lock-screen. Concerned primarily with the quick affordance UI
@@ -51,124 +44,7 @@
         defStyleAttr,
         defStyleRes,
     ) {
-
-    @Deprecated("Deprecated as part of b/278057014")
-    interface MessageDisplayer {
-        fun display(@StringRes stringResourceId: Int)
-    }
-
-    private var ambientIndicationArea: View? = null
-    private var keyguardIndicationArea: View? = null
-    private var binding: KeyguardBottomAreaViewBinder.Binding? = null
-    private var lockIconViewController: LockIconViewController? = null
-    private var isLockscreenLandscapeEnabled: Boolean = false
-
-    /** Initializes the view. */
-    @Deprecated("Deprecated as part of b/278057014")
-    fun init(
-        viewModel: KeyguardBottomAreaViewModel,
-        falsingManager: FalsingManager? = null,
-        lockIconViewController: LockIconViewController? = null,
-        messageDisplayer: MessageDisplayer? = null,
-        vibratorHelper: VibratorHelper? = null,
-        activityStarter: ActivityStarter? = null,
-    ) {
-        binding?.destroy()
-        binding =
-            bind(
-                this,
-                viewModel,
-                falsingManager,
-                vibratorHelper,
-                activityStarter,
-            ) {
-                messageDisplayer?.display(it)
-            }
-        this.lockIconViewController = lockIconViewController
-    }
-
-    /**
-     * Initializes this instance of [KeyguardBottomAreaView] based on the given instance of another
-     * [KeyguardBottomAreaView]
-     */
-    @Deprecated("Deprecated as part of b/278057014")
-    fun initFrom(oldBottomArea: KeyguardBottomAreaView) {
-        // if it exists, continue to use the original ambient indication container
-        // instead of the newly inflated one
-        ambientIndicationArea?.let { nonNullAmbientIndicationArea ->
-            // remove old ambient indication from its parent
-            val originalAmbientIndicationView =
-                oldBottomArea.requireViewById<View>(R.id.ambient_indication_container)
-            (originalAmbientIndicationView.parent as ViewGroup).removeView(
-                originalAmbientIndicationView
-            )
-
-            // remove current ambient indication from its parent (discard)
-            val ambientIndicationParent = nonNullAmbientIndicationArea.parent as ViewGroup
-            val ambientIndicationIndex =
-                ambientIndicationParent.indexOfChild(nonNullAmbientIndicationArea)
-            ambientIndicationParent.removeView(nonNullAmbientIndicationArea)
-
-            // add the old ambient indication to this view
-            ambientIndicationParent.addView(originalAmbientIndicationView, ambientIndicationIndex)
-            ambientIndicationArea = originalAmbientIndicationView
-        }
-    }
-
-    fun setIsLockscreenLandscapeEnabled(isLockscreenLandscapeEnabled: Boolean) {
-        this.isLockscreenLandscapeEnabled = isLockscreenLandscapeEnabled
-    }
-
-    override fun onFinishInflate() {
-        super.onFinishInflate()
-        ambientIndicationArea = findViewById(R.id.ambient_indication_container)
-        keyguardIndicationArea = findViewById(R.id.keyguard_indication_area)
-    }
-
-    override fun onConfigurationChanged(newConfig: Configuration) {
-        super.onConfigurationChanged(newConfig)
-        binding?.onConfigurationChanged()
-
-        if (isLockscreenLandscapeEnabled) {
-            updateIndicationAreaBottomMargin()
-        }
-    }
-
-    private fun updateIndicationAreaBottomMargin() {
-        keyguardIndicationArea?.let {
-            val params = it.layoutParams as FrameLayout.LayoutParams
-            params.bottomMargin =
-                resources.getDimensionPixelSize(R.dimen.keyguard_indication_margin_bottom)
-            it.layoutParams = params
-        }
-    }
-
     override fun hasOverlappingRendering(): Boolean {
         return false
     }
-
-    override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
-        super.onLayout(changed, left, top, right, bottom)
-        findViewById<View>(R.id.ambient_indication_container)?.let {
-            val (ambientLeft, ambientTop) = it.locationOnScreen
-            if (binding?.shouldConstrainToTopOfLockIcon() == true) {
-                // make top of ambient indication view the bottom of the lock icon
-                it.layout(
-                    ambientLeft,
-                    lockIconViewController?.getBottom()?.toInt() ?: 0,
-                    right - ambientLeft,
-                    ambientTop + it.measuredHeight
-                )
-            } else {
-                // make bottom of ambient indication view the top of the lock icon
-                val lockLocationTop = lockIconViewController?.getTop() ?: 0
-                it.layout(
-                    ambientLeft,
-                    lockLocationTop.toInt() - it.measuredHeight,
-                    right - ambientLeft,
-                    lockLocationTop.toInt()
-                )
-            }
-        }
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaViewController.kt
deleted file mode 100644
index 4aece3d..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaViewController.kt
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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
-
-import com.android.systemui.flags.FeatureFlagsClassic
-import com.android.systemui.flags.Flags
-import com.android.systemui.Flags.smartspaceRelocateToBottom
-import android.view.View
-import android.view.ViewGroup
-import android.widget.LinearLayout
-import com.android.systemui.res.R
-import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController
-import com.android.systemui.util.ViewController
-import javax.inject.Inject
-
-class KeyguardBottomAreaViewController
-    @Inject constructor(
-            view: KeyguardBottomAreaView,
-            private val smartspaceController: LockscreenSmartspaceController,
-            featureFlags: FeatureFlagsClassic
-) : ViewController<KeyguardBottomAreaView> (view) {
-
-    private var smartspaceView: View? = null
-
-    init {
-        view.setIsLockscreenLandscapeEnabled(
-                featureFlags.isEnabled(Flags.LOCKSCREEN_ENABLE_LANDSCAPE))
-    }
-
-    override fun onViewAttached() {
-        if (!smartspaceRelocateToBottom() || !smartspaceController.isEnabled) {
-            return
-        }
-
-        val ambientIndicationArea = mView.findViewById<View>(R.id.ambient_indication_container)
-        ambientIndicationArea?.visibility = View.GONE
-
-        addSmartspaceView()
-    }
-
-    override fun onViewDetached() {
-    }
-
-    fun getView(): KeyguardBottomAreaView {
-        // TODO: remove this method.
-        return mView
-    }
-
-    private fun addSmartspaceView() {
-        if (!smartspaceRelocateToBottom()) {
-            return
-        }
-
-        val smartspaceContainer = mView.findViewById<View>(R.id.smartspace_container)
-        smartspaceContainer!!.visibility = View.VISIBLE
-
-        smartspaceView = smartspaceController.buildAndConnectView(smartspaceContainer as ViewGroup)
-        val lp = LinearLayout.LayoutParams(
-                ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
-        (smartspaceContainer as ViewGroup).addView(smartspaceView, 0, lp)
-        val startPadding = context.resources.getDimensionPixelSize(
-                R.dimen.below_clock_padding_start)
-        val endPadding = context.resources.getDimensionPixelSize(
-                R.dimen.below_clock_padding_end)
-        smartspaceView?.setPaddingRelative(startPadding, 0, endPadding, 0)
-//        mKeyguardUnlockAnimationController.lockscreenSmartspace = smartspaceView
-    }
-}
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 f19d707..2467e08 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
@@ -39,10 +39,12 @@
 import com.android.systemui.shade.ShadeLogger
 import com.android.systemui.shade.ShadeViewController
 import com.android.systemui.shade.StatusBarLongPressGestureDetector
+import com.android.systemui.shade.data.repository.ShadeDisplaysRepository
 import com.android.systemui.shade.display.StatusBarTouchShadeDisplayPolicy
 import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor
 import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround
 import com.android.systemui.shared.animation.UnfoldMoveFromCenterAnimator
+import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
 import com.android.systemui.statusbar.data.repository.StatusBarContentInsetsProviderStore
 import com.android.systemui.statusbar.policy.Clock
 import com.android.systemui.statusbar.policy.ConfigurationController
@@ -83,6 +85,7 @@
     private val darkIconDispatcher: DarkIconDispatcher,
     private val statusBarContentInsetsProvider: StatusBarContentInsetsProvider,
     private val lazyStatusBarShadeDisplayPolicy: Lazy<StatusBarTouchShadeDisplayPolicy>,
+    private val shadeDisplaysRepository: ShadeDisplaysRepository,
 ) : ViewController<PhoneStatusBarView>(view) {
 
     private lateinit var battery: BatteryMeterView
@@ -296,7 +299,19 @@
                     return true
                 }
             }
-            return shadeViewController.handleExternalTouch(event)
+
+            // With the StatusBarConnectedDisplays changes, status bar touches should result in
+            // shade interaction only if ShadeWindowGoesAround.isEnabled or if touch is on the
+            // display which currently hosts the shade.
+            return if (
+                !StatusBarConnectedDisplays.isEnabled ||
+                    ShadeWindowGoesAround.isEnabled ||
+                    context.displayId == shadeDisplaysRepository.displayId.value
+            ) {
+                shadeViewController.handleExternalTouch(event)
+            } else {
+                false
+            }
         }
     }
 
@@ -352,6 +367,7 @@
         @DisplaySpecific private val darkIconDispatcher: DarkIconDispatcher,
         private val statusBarContentInsetsProviderStore: StatusBarContentInsetsProviderStore,
         private val lazyStatusBarShadeDisplayPolicy: Lazy<StatusBarTouchShadeDisplayPolicy>,
+        private val shadeDisplaysRepository: ShadeDisplaysRepository,
     ) {
         fun create(view: PhoneStatusBarView): PhoneStatusBarViewController {
             val statusBarMoveFromCenterAnimationController =
@@ -380,6 +396,7 @@
                 darkIconDispatcher,
                 statusBarContentInsetsProviderStore.defaultDisplay,
                 lazyStatusBarShadeDisplayPolicy,
+                shadeDisplaysRepository,
             )
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
index b3cc047..0fac644 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -37,10 +37,12 @@
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.systemui.InitController;
 import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.deviceentry.domain.interactor.DeviceUnlockedInteractor;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
 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.NotificationShadeWindowView;
 import com.android.systemui.shade.QuickSettingsController;
 import com.android.systemui.shade.ShadeViewController;
@@ -59,6 +61,7 @@
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.render.NotifShadeEventSource;
 import com.android.systemui.statusbar.notification.domain.interactor.NotificationAlertsInteractor;
+import com.android.systemui.statusbar.notification.headsup.HeadsUpManager;
 import com.android.systemui.statusbar.notification.interruption.NotificationInterruptSuppressor;
 import com.android.systemui.statusbar.notification.interruption.VisualInterruptionCondition;
 import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProvider;
@@ -69,7 +72,6 @@
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager.OnSettingsClickListener;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
-import com.android.systemui.statusbar.notification.headsup.HeadsUpManager;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 
 import java.util.Set;
@@ -102,6 +104,7 @@
     private final IStatusBarService mBarService;
     private final DynamicPrivacyController mDynamicPrivacyController;
     private final NotificationListContainer mNotifListContainer;
+    private final DeviceUnlockedInteractor mDeviceUnlockedInteractor;
     private final QuickSettingsController mQsController;
 
     protected boolean mVrMode;
@@ -133,7 +136,8 @@
             VisualInterruptionDecisionProvider visualInterruptionDecisionProvider,
             NotificationRemoteInputManager remoteInputManager,
             NotificationRemoteInputManager.Callback remoteInputManagerCallback,
-            NotificationListContainer notificationListContainer) {
+            NotificationListContainer notificationListContainer,
+            DeviceUnlockedInteractor deviceUnlockedInteractor) {
         mActivityStarter = activityStarter;
         mKeyguardStateController = keyguardStateController;
         mNotificationPanel = panel;
@@ -160,6 +164,7 @@
         mBarService = IStatusBarService.Stub.asInterface(
                 ServiceManager.getService(Context.STATUS_BAR_SERVICE));
         mNotifListContainer = notificationListContainer;
+        mDeviceUnlockedInteractor = deviceUnlockedInteractor;
 
         IVrManager vrManager = IVrManager.Stub.asInterface(ServiceManager.getService(
                 Context.VR_SERVICE));
@@ -248,14 +253,25 @@
             if (mStatusBarStateController.getState() == StatusBarState.KEYGUARD) {
                 mShadeTransitionController.goToLockedShade(clickedEntry.getRow());
             } else if (clickedEntry.isSensitive().getValue()
-                    && mDynamicPrivacyController.isInLockedDownShade()) {
+                    && isInLockedDownShade()) {
                 mStatusBarStateController.setLeaveOpenOnKeyguardHide(true);
+                // launch the bouncer
                 mActivityStarter.dismissKeyguardThenExecute(() -> false /* dismissAction */
                         , null /* cancelRunnable */, false /* afterKeyguardGone */);
             }
         }
     }
 
+    /** @return true if the Shade is shown over the Lockscreen, and the device is locked */
+    private boolean isInLockedDownShade() {
+        if (SceneContainerFlag.isEnabled()) {
+            return mStatusBarStateController.getState() == StatusBarState.SHADE_LOCKED
+                    && !mDeviceUnlockedInteractor.getDeviceUnlockStatus().getValue().isUnlocked();
+        } else {
+            return mDynamicPrivacyController.isInLockedDownShade();
+        }
+    }
+
     @Override
     public boolean isDeviceInVrMode() {
         return mVrMode;
@@ -300,7 +316,8 @@
                         .isLockscreenPublicMode(mLockscreenUserManager.getCurrentUserId());
                 boolean userPublic = devicePublic
                         || mLockscreenUserManager.isLockscreenPublicMode(sbn.getUserId());
-                boolean needsRedaction = mLockscreenUserManager.needsRedaction(entry);
+                boolean needsRedaction = mLockscreenUserManager.getRedactionType(entry)
+                        != NotificationLockscreenUserManager.REDACTION_TYPE_NONE;
                 if (userPublic && needsRedaction) {
                     // TODO(b/135046837): we can probably relax this with dynamic privacy
                     return true;
@@ -353,7 +370,8 @@
                         return false;
                     }
 
-                    if (!mLockscreenUserManager.needsRedaction(entry)) {
+                    if (mLockscreenUserManager.getRedactionType(entry)
+                            == NotificationLockscreenUserManager.REDACTION_TYPE_NONE) {
                         return false;
                     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
index 0ad1042a..03324d2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
@@ -145,7 +145,7 @@
          */
         public SystemUIDialog create() {
             return create(new DialogDelegate<>() {
-            }, mContext, DEFAULT_THEME);
+            }, mContext, DEFAULT_THEME, true /* shouldAcsdDismissDialog */);
         }
 
         /**
@@ -155,7 +155,7 @@
          */
         public SystemUIDialog create(Context context) {
             return create(new DialogDelegate<>() {
-            }, context, DEFAULT_THEME);
+            }, context, DEFAULT_THEME, true /* shouldAcsdDismissDialog */);
         }
 
         /**
@@ -168,8 +168,21 @@
             return create(delegate, context, DEFAULT_THEME);
         }
 
+        /**
+         * Creates a new instance of {@link SystemUIDialog} with {@code delegate} as the {@link
+         * Delegate}. When you need to customize the dialog, pass it a delegate.
+         *
+         * This method allows the caller to specify if the dialog should be dismissed in response
+         * to the ACTION_CLOSE_SYSTEM_DIALOGS intent.
+         */
+        public SystemUIDialog create(Delegate delegate, Context context,
+                boolean shouldAcsdDismissDialog) {
+            return create(delegate, context, DEFAULT_THEME, shouldAcsdDismissDialog);
+        }
+
         public SystemUIDialog create(Delegate delegate, Context context, @StyleRes int theme) {
-            return create((DialogDelegate<SystemUIDialog>) delegate, context, theme);
+            return create((DialogDelegate<SystemUIDialog>) delegate, context, theme,
+                    true /* shouldAcsdDismissDialog */);
         }
 
         public SystemUIDialog create(Delegate delegate) {
@@ -177,7 +190,7 @@
         }
 
         private SystemUIDialog create(DialogDelegate<SystemUIDialog> dialogDelegate,
-                Context context, @StyleRes int theme) {
+                Context context, @StyleRes int theme, boolean shouldAcsdDismissDialog) {
             return new SystemUIDialog(
                     context,
                     theme,
@@ -186,7 +199,8 @@
                     mSysUiState,
                     mBroadcastDispatcher,
                     mDialogTransitionAnimator,
-                    dialogDelegate);
+                    dialogDelegate,
+                    shouldAcsdDismissDialog);
         }
     }
 
@@ -207,7 +221,8 @@
                 broadcastDispatcher,
                 dialogTransitionAnimator,
                 new DialogDelegate<>() {
-                });
+                },
+                true /* shouldAcsdDismissDialog */);
     }
 
     public SystemUIDialog(
@@ -227,7 +242,8 @@
                 sysUiState,
                 broadcastDispatcher,
                 dialogTransitionAnimator,
-                (DialogDelegate<SystemUIDialog>) delegate);
+                (DialogDelegate<SystemUIDialog>) delegate,
+                true /* shouldAcsdDismissDialog */);
     }
 
     public SystemUIDialog(
@@ -238,7 +254,8 @@
             SysUiState sysUiState,
             BroadcastDispatcher broadcastDispatcher,
             DialogTransitionAnimator dialogTransitionAnimator,
-            DialogDelegate<SystemUIDialog> delegate) {
+            DialogDelegate<SystemUIDialog> delegate,
+            boolean shouldAcsdDismissDialog) {
         super(context, theme);
         mContext = context;
         mDelegate = delegate;
@@ -249,7 +266,7 @@
         getWindow().setAttributes(attrs);
 
         mDismissReceiver = dismissOnDeviceLock ? new DismissReceiver(this, broadcastDispatcher,
-                dialogTransitionAnimator) : null;
+                dialogTransitionAnimator, shouldAcsdDismissDialog) : null;
         mDialogManager = dialogManager;
         mSysUiState = sysUiState;
     }
@@ -523,7 +540,8 @@
         // TODO(b/219008720): Remove those calls to Dependency.get.
         DismissReceiver dismissReceiver = new DismissReceiver(dialog,
                 Dependency.get(BroadcastDispatcher.class),
-                Dependency.get(DialogTransitionAnimator.class));
+                Dependency.get(DialogTransitionAnimator.class),
+                true /* shouldAcsdDismissDialog */);
         dialog.setOnDismissListener(d -> {
             dismissReceiver.unregister();
             if (dismissAction != null) dismissAction.run();
@@ -595,12 +613,7 @@
     }
 
     private static class DismissReceiver extends BroadcastReceiver {
-        private static final IntentFilter INTENT_FILTER = new IntentFilter();
-
-        static {
-            INTENT_FILTER.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
-            INTENT_FILTER.addAction(Intent.ACTION_SCREEN_OFF);
-        }
+        private final IntentFilter mIntentFilter = new IntentFilter();
 
         private final Dialog mDialog;
         private boolean mRegistered;
@@ -608,14 +621,20 @@
         private final DialogTransitionAnimator mDialogTransitionAnimator;
 
         DismissReceiver(Dialog dialog, BroadcastDispatcher broadcastDispatcher,
-                DialogTransitionAnimator dialogTransitionAnimator) {
+                DialogTransitionAnimator dialogTransitionAnimator,
+                boolean shouldAcsdDismissDialog) {
             mDialog = dialog;
             mBroadcastDispatcher = broadcastDispatcher;
             mDialogTransitionAnimator = dialogTransitionAnimator;
+
+            mIntentFilter.addAction(Intent.ACTION_SCREEN_OFF);
+            if (shouldAcsdDismissDialog) {
+                mIntentFilter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
+            }
         }
 
         void register() {
-            mBroadcastDispatcher.registerReceiver(this, INTENT_FILTER, null, UserHandle.CURRENT);
+            mBroadcastDispatcher.registerReceiver(this, mIntentFilter, null, UserHandle.CURRENT);
             mRegistered = true;
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
index 724ba8c..d257288 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
@@ -627,8 +627,9 @@
         StatusBarRootModernization.assertInLegacyMode();
 
         // TODO(b/328393714) use HeadsUpNotificationInteractor.showHeadsUpStatusBar instead.
-        boolean headsUpVisible =
-                mHomeStatusBarComponent.getHeadsUpAppearanceController().shouldBeVisible();
+        boolean headsUpVisible = mHomeStatusBarComponent
+                .getHeadsUpAppearanceController()
+                .shouldHeadsUpStatusBarBeVisible();
 
         if (SceneContainerFlag.isEnabled()) {
             // With the scene container, only use the value calculated by the view model to
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractor.kt
index 4b71c02..2f7b243 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractor.kt
@@ -16,22 +16,34 @@
 
 package com.android.systemui.statusbar.phone.ongoingcall.domain.interactor
 
+import androidx.annotation.VisibleForTesting
+import com.android.systemui.CoreStartable
 import com.android.systemui.activity.data.repository.ActivityManagerRepository
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.log.LogBuffer
 import com.android.systemui.log.core.Logger
+import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryStore
+import com.android.systemui.statusbar.gesture.SwipeStatusBarAwayGestureHandler
 import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor
+import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel
 import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallLog
 import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
+import com.android.systemui.statusbar.window.StatusBarWindowControllerStore
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
 import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.filterIsInstance
 import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.flow.stateIn
 
 /**
@@ -47,57 +59,146 @@
 @SysUISingleton
 class OngoingCallInteractor @Inject constructor(
     @Application private val scope: CoroutineScope,
-    activityManagerRepository: ActivityManagerRepository,
+    private val activityManagerRepository: ActivityManagerRepository,
+    private val statusBarModeRepositoryStore: StatusBarModeRepositoryStore,
+    private val statusBarWindowControllerStore: StatusBarWindowControllerStore,
+    private val swipeStatusBarAwayGestureHandler: SwipeStatusBarAwayGestureHandler,
     activeNotificationsInteractor: ActiveNotificationsInteractor,
     @OngoingCallLog private val logBuffer: LogBuffer,
-) {
+) : CoreStartable {
     private val logger = Logger(logBuffer, TAG)
 
     /**
+     * Tracks whether the call chip has been swiped away.
+     */
+    private val _isChipSwipedAway = MutableStateFlow(false)
+    val isChipSwipedAway: StateFlow<Boolean> = _isChipSwipedAway.asStateFlow()
+
+    /**
      * The current state of ongoing calls.
      */
     val ongoingCallState: StateFlow<OngoingCallModel> =
         activeNotificationsInteractor.ongoingCallNotification
-            .flatMapLatest { notificationModel ->
-                when (notificationModel) {
-                    null -> {
-                        logger.d("No active call notification - hiding chip")
-                        flowOf(OngoingCallModel.NoCall)
-                    }
-
-                    else -> combine(
-                        flowOf(notificationModel),
-                        activityManagerRepository.createIsAppVisibleFlow(
-                            creationUid = notificationModel.uid,
-                            logger = logger,
-                            identifyingLogTag = TAG,
-                        ),
-                    ) { model, isVisible ->
-                        when {
-                            isVisible -> {
-                                logger.d({ "Call app is visible: uid=$int1" }) {
-                                    int1 = model.uid
-                                }
-                                OngoingCallModel.InCallWithVisibleApp
-                            }
-
-                            else -> {
-                                logger.d({ "Active call detected: startTime=$long1 hasIcon=$bool1" }) {
-                                    long1 = model.whenTime
-                                    bool1 = model.statusBarChipIconView != null
-                                }
-                                OngoingCallModel.InCall(
-                                    startTimeMs = model.whenTime,
-                                    notificationIconView = model.statusBarChipIconView,
-                                    intent = model.contentIntent,
-                                    notificationKey = model.key,
-                                )
-                            }
-                        }
-                    }
-                }
+            .flatMapLatest { notification ->
+                createOngoingCallStateFlow(
+                    notification = notification
+                )
             }
-            .stateIn(scope, SharingStarted.WhileSubscribed(), OngoingCallModel.NoCall)
+            .stateIn(
+                scope = scope,
+                started = SharingStarted.WhileSubscribed(),
+                initialValue = OngoingCallModel.NoCall
+            )
+
+    @VisibleForTesting
+    val isStatusBarRequiredForOngoingCall = combine(
+        ongoingCallState,
+        isChipSwipedAway
+    ) { callState, chipSwipedAway ->
+        callState is OngoingCallModel.InCall && !chipSwipedAway
+    }
+
+    @VisibleForTesting
+    val isGestureListeningEnabled = combine(
+        ongoingCallState,
+        statusBarModeRepositoryStore.defaultDisplay.isInFullscreenMode,
+        isChipSwipedAway
+    ) { callState, isFullscreen, chipSwipedAway ->
+        callState is OngoingCallModel.InCall && !chipSwipedAway && isFullscreen
+    }
+
+    private fun createOngoingCallStateFlow(
+        notification: ActiveNotificationModel?
+    ): Flow<OngoingCallModel> {
+        if (notification == null) {
+            logger.d("No active call notification - hiding chip")
+            return flowOf(OngoingCallModel.NoCall)
+        }
+
+        return combine(
+            flowOf(notification),
+            activityManagerRepository.createIsAppVisibleFlow(
+                creationUid = notification.uid,
+                logger = logger,
+                identifyingLogTag = TAG,
+            )
+        ) { model, isVisible ->
+            deriveOngoingCallState(model, isVisible)
+        }
+    }
+
+    override fun start() {
+        ongoingCallState
+            .filterIsInstance<OngoingCallModel.NoCall>()
+            .onEach {
+                _isChipSwipedAway.value = false
+            }.launchIn(scope)
+
+        isStatusBarRequiredForOngoingCall.onEach { statusBarRequired ->
+            setStatusBarRequiredForOngoingCall(statusBarRequired)
+        }.launchIn(scope)
+
+        isGestureListeningEnabled.onEach { isEnabled ->
+            updateGestureListening(isEnabled)
+        }.launchIn(scope)
+    }
+
+    /**
+     * Callback that must run when the status bar is swiped while gesture listening is active.
+     */
+    @VisibleForTesting
+    fun onStatusBarSwiped() {
+        logger.d("Status bar chip swiped away")
+        _isChipSwipedAway.value = true
+    }
+
+    private fun deriveOngoingCallState(
+        model: ActiveNotificationModel,
+        isVisible: Boolean
+    ): OngoingCallModel {
+        return when {
+            isVisible -> {
+                logger.d({ "Call app is visible: uid=$int1" }) {
+                    int1 = model.uid
+                }
+                OngoingCallModel.InCallWithVisibleApp
+            }
+
+            else -> {
+                logger.d({ "Active call detected: startTime=$long1 hasIcon=$bool1" }) {
+                    long1 = model.whenTime
+                    bool1 = model.statusBarChipIconView != null
+                }
+                OngoingCallModel.InCall(
+                    startTimeMs = model.whenTime,
+                    notificationIconView = model.statusBarChipIconView,
+                    intent = model.contentIntent,
+                    notificationKey = model.key
+                )
+            }
+        }
+    }
+
+    private fun setStatusBarRequiredForOngoingCall(statusBarRequired: Boolean) {
+        // TODO(b/382808183): Create a single repository that can be utilized in
+        //  `statusBarModeRepositoryStore` and `statusBarWindowControllerStore` so we do not need
+        //  two separate calls to force the status bar to stay visible.
+        statusBarModeRepositoryStore.defaultDisplay.setOngoingProcessRequiresStatusBarVisible(
+            statusBarRequired
+        )
+        statusBarWindowControllerStore.defaultDisplay
+            .setOngoingProcessRequiresStatusBarVisible(statusBarRequired)
+    }
+
+    private fun updateGestureListening(isEnabled: Boolean) {
+        if (isEnabled) {
+            swipeStatusBarAwayGestureHandler.addOnGestureDetectedCallback(TAG) { _ ->
+                onStatusBarSwiped()
+            }
+        } else {
+            swipeStatusBarAwayGestureHandler.removeOnGestureDetectedCallback(TAG)
+        }
+    }
 
     companion object {
         private val TAG = "OngoingCall"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileContentDescriptionViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileContentDescriptionViewBinder.kt
new file mode 100644
index 0000000..c720b1d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileContentDescriptionViewBinder.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.pipeline.mobile.ui.binder
+
+import android.view.View
+import com.android.systemui.statusbar.pipeline.mobile.ui.model.MobileContentDescription
+
+object MobileContentDescriptionViewBinder {
+    fun bind(contentDescription: MobileContentDescription?, view: View) {
+        view.contentDescription =
+            when (contentDescription) {
+                null -> null
+                else -> contentDescription.loadContentDescription(view.context)
+            }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt
index 31d349e..788f041 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt
@@ -29,9 +29,9 @@
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.lifecycleScope
 import androidx.lifecycle.repeatOnLifecycle
+import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.settingslib.graph.SignalDrawable
 import com.android.systemui.Flags.statusBarStaticInoutIndicators
-import com.android.systemui.common.ui.binder.ContentDescriptionViewBinder
 import com.android.systemui.common.ui.binder.IconViewBinder
 import com.android.systemui.lifecycle.repeatWhenAttached
 import com.android.systemui.plugins.DarkIconDispatcher
@@ -48,12 +48,8 @@
 import kotlinx.coroutines.awaitCancellation
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.distinctUntilChanged
-import com.android.app.tracing.coroutines.launchTraced as launch
 
-private data class Colors(
-    @ColorInt val tint: Int,
-    @ColorInt val contrast: Int,
-)
+private data class Colors(@ColorInt val tint: Int, @ColorInt val contrast: Int)
 
 object MobileIconBinder {
     /** Binds the view to the view-model, continuing to update the former based on the latter */
@@ -87,7 +83,7 @@
             MutableStateFlow(
                 Colors(
                     tint = DarkIconDispatcher.DEFAULT_ICON_TINT,
-                    contrast = DarkIconDispatcher.DEFAULT_INVERSE_ICON_TINT
+                    contrast = DarkIconDispatcher.DEFAULT_INVERSE_ICON_TINT,
                 )
             )
         val decorTint: MutableStateFlow<Int> = MutableStateFlow(viewModel.defaultColor)
@@ -105,7 +101,7 @@
                             viewModel.verboseLogger?.logBinderReceivedVisibility(
                                 view,
                                 viewModel.subscriptionId,
-                                isVisible
+                                isVisible,
                             )
                             view.isVisible = isVisible
                             // [StatusIconContainer] can get out of sync sometimes. Make sure to
@@ -152,7 +148,7 @@
 
                     launch {
                         viewModel.contentDescription.distinctUntilChanged().collect {
-                            ContentDescriptionViewBinder.bind(it, view)
+                            MobileContentDescriptionViewBinder.bind(it, view)
                         }
                     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/model/MobileContentDescription.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/model/MobileContentDescription.kt
new file mode 100644
index 0000000..84fa073
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/model/MobileContentDescription.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.statusbar.pipeline.mobile.ui.model
+
+import android.annotation.StringRes
+import android.content.Context
+import com.android.systemui.res.R
+
+sealed interface MobileContentDescription {
+    fun loadContentDescription(context: Context): String
+
+    /**
+     * Content description for cellular parameterizes the [networkName] which comes from the system
+     */
+    data class Cellular(val networkName: String, @StringRes val levelDescriptionRes: Int) :
+        MobileContentDescription {
+        override fun loadContentDescription(context: Context): String =
+            context.getString(
+                R.string.accessibility_phone_string_format,
+                networkName,
+                context.getString(levelDescriptionRes),
+            )
+    }
+
+    data class SatelliteContentDescription(@StringRes val resId: Int) : MobileContentDescription {
+        override fun loadContentDescription(context: Context): String =
+            context.getString(this.resId)
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt
index 103b0e3..0bd3426 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt
@@ -16,7 +16,6 @@
 
 package com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel
 
-import com.android.settingslib.AccessibilityContentDescriptions
 import com.android.systemui.Flags.statusBarStaticInoutIndicators
 import com.android.systemui.common.shared.model.ContentDescription
 import com.android.systemui.common.shared.model.Icon
@@ -28,6 +27,7 @@
 import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconInteractor
 import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractor
 import com.android.systemui.statusbar.pipeline.mobile.domain.model.SignalIconModel
+import com.android.systemui.statusbar.pipeline.mobile.ui.model.MobileContentDescription
 import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
 import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
 import kotlinx.coroutines.CoroutineScope
@@ -50,7 +50,7 @@
     /** True if this view should be visible at all. */
     val isVisible: StateFlow<Boolean>
     val icon: Flow<SignalIconModel>
-    val contentDescription: Flow<ContentDescription?>
+    val contentDescription: Flow<MobileContentDescription?>
     val roaming: Flow<Boolean>
     /** The RAT icon (LTE, 3G, 5G, etc) to be displayed. Null if we shouldn't show anything */
     val networkTypeIcon: Flow<Icon.Resource?>
@@ -95,10 +95,7 @@
     }
 
     private val satelliteProvider by lazy {
-        CarrierBasedSatelliteViewModelImpl(
-            subscriptionId,
-            iconInteractor,
-        )
+        CarrierBasedSatelliteViewModelImpl(subscriptionId, iconInteractor)
     }
 
     /**
@@ -123,7 +120,7 @@
 
     override val icon: Flow<SignalIconModel> = vmProvider.flatMapLatest { it.icon }
 
-    override val contentDescription: Flow<ContentDescription?> =
+    override val contentDescription: Flow<MobileContentDescription?> =
         vmProvider.flatMapLatest { it.contentDescription }
 
     override val roaming: Flow<Boolean> = vmProvider.flatMapLatest { it.roaming }
@@ -154,8 +151,7 @@
     override val isVisible: StateFlow<Boolean> = MutableStateFlow(true)
     override val icon: Flow<SignalIconModel> = interactor.signalLevelIcon
 
-    override val contentDescription: Flow<ContentDescription> =
-        MutableStateFlow(ContentDescription.Loaded(""))
+    override val contentDescription: Flow<MobileContentDescription?> = MutableStateFlow(null)
 
     /** These fields are not used for satellite icons currently */
     override val roaming: Flow<Boolean> = flowOf(false)
@@ -206,27 +202,42 @@
 
     override val icon: Flow<SignalIconModel> = iconInteractor.signalLevelIcon
 
-    override val contentDescription: Flow<ContentDescription?> =
-        iconInteractor.signalLevelIcon
-            .map {
-                // We expect the signal icon to be cellular here since this is the cellular vm
-                if (it !is SignalIconModel.Cellular) {
-                    null
-                } else {
-                    val resId =
-                        AccessibilityContentDescriptions.getDescriptionForLevel(
-                            it.level,
-                            it.numberOfLevels
+    override val contentDescription: Flow<MobileContentDescription?> =
+        combine(iconInteractor.signalLevelIcon, iconInteractor.networkName) { icon, nameModel ->
+                when (icon) {
+                    is SignalIconModel.Cellular ->
+                        MobileContentDescription.Cellular(
+                            nameModel.name,
+                            icon.levelDescriptionRes(),
                         )
-                    if (resId != 0) {
-                        ContentDescription.Resource(resId)
-                    } else {
-                        null
-                    }
+                    else -> null
                 }
             }
             .stateIn(scope, SharingStarted.WhileSubscribed(), null)
 
+    private fun SignalIconModel.Cellular.levelDescriptionRes() =
+        when (level) {
+            0 -> R.string.accessibility_no_signal
+            1 -> R.string.accessibility_one_bar
+            2 -> R.string.accessibility_two_bars
+            3 -> R.string.accessibility_three_bars
+            4 -> {
+                if (numberOfLevels == 6) {
+                    R.string.accessibility_four_bars
+                } else {
+                    R.string.accessibility_signal_full
+                }
+            }
+            5 -> {
+                if (numberOfLevels == 6) {
+                    R.string.accessibility_signal_full
+                } else {
+                    R.string.accessibility_no_signal
+                }
+            }
+            else -> R.string.accessibility_no_signal
+        }
+
     private val showNetworkTypeIcon: Flow<Boolean> =
         combine(
                 iconInteractor.isDataConnected,
@@ -248,10 +259,9 @@
             .stateIn(scope, SharingStarted.WhileSubscribed(), false)
 
     override val networkTypeIcon: Flow<Icon.Resource?> =
-        combine(
-                iconInteractor.networkTypeIconGroup,
-                showNetworkTypeIcon,
-            ) { networkTypeIconGroup, shouldShow ->
+        combine(iconInteractor.networkTypeIconGroup, showNetworkTypeIcon) {
+                networkTypeIconGroup,
+                shouldShow ->
                 val desc =
                     if (networkTypeIconGroup.contentDescription != 0)
                         ContentDescription.Resource(networkTypeIconGroup.contentDescription)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt
index a36ef56..f11ebc0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt
@@ -220,7 +220,7 @@
 
         if (satelliteManager != null) {
             // Outer scope launch allows us to delay until MIN_UPTIME
-            scope.launch {
+            scope.launch(context = bgDispatcher) {
                 // First, check that satellite is supported on this device
                 satelliteSupport.value = checkSatelliteSupportAfterMinUptime(satelliteManager)
                 logBuffer.i(
@@ -229,7 +229,9 @@
                 )
 
                 // Second, register a listener to let us know if there are changes to support
-                scope.launch { listenForChangesToSatelliteSupport(satelliteManager) }
+                scope.launch(context = bgDispatcher) {
+                    listenForChangesToSatelliteSupport(satelliteManager)
+                }
             }
         } else {
             logBuffer.i { "Satellite manager is null" }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt
index c52275a9..6e9e1ec 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt
@@ -41,6 +41,7 @@
 import com.android.systemui.statusbar.events.shared.model.SystemEventAnimationState.Idle
 import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor
 import com.android.systemui.statusbar.notification.domain.interactor.HeadsUpNotificationInteractor
+import com.android.systemui.statusbar.notification.headsup.PinnedStatus
 import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor
 import com.android.systemui.statusbar.phone.domain.interactor.LightsOutInteractor
 import com.android.systemui.statusbar.pipeline.shared.domain.interactor.CollapsedStatusBarInteractor
@@ -235,14 +236,18 @@
     override val isClockVisible: Flow<VisibilityModel> =
         combine(
             shouldHomeStatusBarBeVisible,
-            headsUpNotificationInteractor.showHeadsUpStatusBar,
+            headsUpNotificationInteractor.statusBarHeadsUpState,
             collapsedStatusBarInteractor.visibilityViaDisableFlags,
-        ) { shouldStatusBarBeVisible, showHeadsUp, visibilityViaDisableFlags ->
+        ) { shouldStatusBarBeVisible, headsUpState, visibilityViaDisableFlags ->
+            val hideClockForHeadsUp = headsUpState == PinnedStatus.PinnedBySystem
             val showClock =
-                shouldStatusBarBeVisible && visibilityViaDisableFlags.isClockAllowed && !showHeadsUp
+                shouldStatusBarBeVisible &&
+                    visibilityViaDisableFlags.isClockAllowed &&
+                    !hideClockForHeadsUp
             // Always use View.INVISIBLE here, so that animations work
             VisibilityModel(showClock.toVisibleOrInvisible(), visibilityViaDisableFlags.animate)
         }
+
     override val isNotificationIconContainerVisible: Flow<VisibilityModel> =
         combine(
             shouldHomeStatusBarBeVisible,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastController.java
index ece5a3f..a3dcc3b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastController.java
@@ -16,7 +16,6 @@
 
 package com.android.systemui.statusbar.policy;
 
-import android.media.projection.StopReason;
 import com.android.systemui.Dumpable;
 import com.android.systemui.statusbar.policy.CastController.Callback;
 
@@ -27,7 +26,7 @@
     void setCurrentUserId(int currentUserId);
     List<CastDevice> getCastDevices();
     void startCasting(CastDevice device);
-    void stopCasting(CastDevice device, @StopReason int stopReason);
+    void stopCasting(CastDevice device);
 
     /**
      * @return whether we have a connected device.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java
index ab20850..52f80fb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java
@@ -185,13 +185,13 @@
     }
 
     @Override
-    public void stopCasting(CastDevice device, @StopReason int stopReason) {
+    public void stopCasting(CastDevice device) {
         final boolean isProjection = device.getTag() instanceof MediaProjectionInfo;
         mLogger.logStopCasting(isProjection);
         if (isProjection) {
             final MediaProjectionInfo projection = (MediaProjectionInfo) device.getTag();
             if (Objects.equals(mProjectionManager.getActiveProjectionInfo(), projection)) {
-                mProjectionManager.stopActiveProjection(stopReason);
+                mProjectionManager.stopActiveProjection(StopReason.STOP_QS_TILE);
             } else {
                 mLogger.logStopCastingNoProjection(projection);
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationController.java
index 1bb4e8c..c77f6c1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationController.java
@@ -45,5 +45,6 @@
         default void onLocaleListChanged() {}
         default void onLayoutDirectionChanged(boolean isLayoutRtl) {}
         default void onOrientationChanged(int orientation) {}
+        default void onMovedToDisplay(int newDisplayId, Configuration newConfiguration) {}
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModel.kt
index fe1d647..6175ea1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModel.kt
@@ -35,6 +35,7 @@
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.stateIn
 
 /**
@@ -59,7 +60,7 @@
 
     private val showingHeadsUpStatusBar: Flow<Boolean> =
         if (SceneContainerFlag.isEnabled) {
-            headsUpNotificationInteractor.showHeadsUpStatusBar
+            headsUpNotificationInteractor.statusBarHeadsUpState.map { it.isPinned }
         } else {
             flowOf(false)
         }
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/BackGestureTutorialScreen.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/BackGestureTutorialScreen.kt
index bfdae62..e1cc11a 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/BackGestureTutorialScreen.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/BackGestureTutorialScreen.kt
@@ -45,6 +45,8 @@
                     bodyResId = R.string.touchpad_back_gesture_guidance,
                     titleSuccessResId = R.string.touchpad_back_gesture_success_title,
                     bodySuccessResId = R.string.touchpad_back_gesture_success_body,
+                    titleErrorResId = R.string.gesture_error_title,
+                    bodyErrorResId = R.string.touchpad_back_gesture_error_body,
                 ),
             animations = TutorialScreenConfig.Animations(educationResId = R.raw.trackpad_back_edu),
         )
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/GestureTutorialScreen.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/GestureTutorialScreen.kt
index 2332c00..ed84f9c 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/GestureTutorialScreen.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/GestureTutorialScreen.kt
@@ -54,6 +54,8 @@
         val progressStartMarker: String,
         val progressEndMarker: String,
     ) : GestureUiState
+
+    data object Error : GestureUiState
 }
 
 fun GestureState.toGestureUiState(
@@ -66,19 +68,31 @@
         is GestureState.InProgress ->
             GestureUiState.InProgress(this.progress, progressStartMarker, progressEndMarker)
         is GestureState.Finished -> GestureUiState.Finished(successAnimation)
+        GestureState.Error -> GestureUiState.Error
     }
 }
 
-fun GestureUiState.toTutorialActionState(): TutorialActionState {
+fun GestureUiState.toTutorialActionState(previousState: TutorialActionState): TutorialActionState {
     return when (this) {
         NotStarted -> TutorialActionState.NotStarted
-        is GestureUiState.InProgress ->
-            TutorialActionState.InProgress(
-                progress = progress,
-                startMarker = progressStartMarker,
-                endMarker = progressEndMarker,
-            )
+        is GestureUiState.InProgress -> {
+            val inProgress =
+                TutorialActionState.InProgress(
+                    progress = progress,
+                    startMarker = progressStartMarker,
+                    endMarker = progressEndMarker,
+                )
+            if (
+                previousState is TutorialActionState.InProgressAfterError ||
+                    previousState is TutorialActionState.Error
+            ) {
+                return TutorialActionState.InProgressAfterError(inProgress)
+            } else {
+                return inProgress
+            }
+        }
         is Finished -> TutorialActionState.Finished(successAnimation)
+        GestureUiState.Error -> TutorialActionState.Error
     }
 }
 
@@ -102,11 +116,11 @@
         easterEggTriggered,
         resetEasterEggFlag = { easterEggTriggered = false },
     ) {
-        ActionTutorialContent(
-            gestureState.toTutorialActionState(),
-            onDoneButtonClicked,
-            screenConfig,
-        )
+        var lastState: TutorialActionState by remember {
+            mutableStateOf(TutorialActionState.NotStarted)
+        }
+        lastState = gestureState.toTutorialActionState(lastState)
+        ActionTutorialContent(lastState, onDoneButtonClicked, screenConfig)
     }
 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/HomeGestureTutorialScreen.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/HomeGestureTutorialScreen.kt
index 45fdd21..26604ca 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/HomeGestureTutorialScreen.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/HomeGestureTutorialScreen.kt
@@ -42,6 +42,8 @@
                     bodyResId = R.string.touchpad_home_gesture_guidance,
                     titleSuccessResId = R.string.touchpad_home_gesture_success_title,
                     bodySuccessResId = R.string.touchpad_home_gesture_success_body,
+                    titleErrorResId = R.string.gesture_error_title,
+                    bodyErrorResId = R.string.touchpad_home_gesture_error_body,
                 ),
             animations = TutorialScreenConfig.Animations(educationResId = R.raw.trackpad_home_edu),
         )
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/RecentAppsGestureTutorialScreen.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/RecentAppsGestureTutorialScreen.kt
index 680b670..6400aca 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/RecentAppsGestureTutorialScreen.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/RecentAppsGestureTutorialScreen.kt
@@ -42,6 +42,8 @@
                     bodyResId = R.string.touchpad_recent_apps_gesture_guidance,
                     titleSuccessResId = R.string.touchpad_recent_apps_gesture_success_title,
                     bodySuccessResId = R.string.touchpad_recent_apps_gesture_success_body,
+                    titleErrorResId = R.string.gesture_error_title,
+                    bodyErrorResId = R.string.touchpad_recent_gesture_error_body,
                 ),
             animations =
                 TutorialScreenConfig.Animations(educationResId = R.raw.trackpad_recent_apps_edu),
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureRecognizer.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureRecognizer.kt
index 35ea0ea..64cda1f2 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureRecognizer.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureRecognizer.kt
@@ -41,7 +41,13 @@
     }
 
     override fun accept(event: MotionEvent) {
-        if (!isThreeFingerTouchpadSwipe(event)) return
+        if (!isMultifingerTouchpadSwipe(event)) return
+        if (!isThreeFingerTouchpadSwipe(event)) {
+            if (event.actionMasked == MotionEvent.ACTION_UP) {
+                gestureStateChangedCallback(GestureState.Error)
+            }
+            return
+        }
         val gestureState = distanceTracker.processEvent(event)
         updateGestureState(
             gestureStateChangedCallback,
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/GestureRecognizer.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/GestureRecognizer.kt
index 68a2ef9..884f08e 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/GestureRecognizer.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/GestureRecognizer.kt
@@ -35,6 +35,11 @@
         event.getAxisValue(MotionEvent.AXIS_GESTURE_SWIPE_FINGER_COUNT) == fingerCount.toFloat()
 }
 
+fun isMultifingerTouchpadSwipe(event: MotionEvent): Boolean {
+    return event.classification == MotionEvent.CLASSIFICATION_MULTI_FINGER_SWIPE ||
+        event.classification == MotionEvent.CLASSIFICATION_TWO_FINGER_SWIPE
+}
+
 fun isTwoFingerSwipe(event: MotionEvent): Boolean {
     return event.classification == MotionEvent.CLASSIFICATION_TWO_FINGER_SWIPE
 }
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/GestureState.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/GestureState.kt
index f27ddb5..7bc7e81 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/GestureState.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/GestureState.kt
@@ -23,6 +23,8 @@
 
     data class InProgress(val progress: Float = 0f, val direction: GestureDirection? = null) :
         GestureState
+
+    data object Error : GestureState
 }
 
 enum class GestureDirection {
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/GestureStateUpdates.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/GestureStateUpdates.kt
index 24f5d1f..69886e4 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/GestureStateUpdates.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/GestureStateUpdates.kt
@@ -28,7 +28,7 @@
             if (isFinished(gestureState)) {
                 gestureStateChangedCallback(GestureState.Finished)
             } else {
-                gestureStateChangedCallback(GestureState.NotStarted)
+                gestureStateChangedCallback(GestureState.Error)
             }
         }
         is Moving -> {
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureRecognizer.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureRecognizer.kt
index 9801626..7767a46 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureRecognizer.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureRecognizer.kt
@@ -40,7 +40,13 @@
     }
 
     override fun accept(event: MotionEvent) {
-        if (!isThreeFingerTouchpadSwipe(event)) return
+        if (!isMultifingerTouchpadSwipe(event)) return
+        if (!isThreeFingerTouchpadSwipe(event)) {
+            if (event.actionMasked == MotionEvent.ACTION_UP) {
+                gestureStateChangedCallback(GestureState.Error)
+            }
+            return
+        }
         val gestureState = distanceTracker.processEvent(event)
         velocityTracker.accept(event)
         updateGestureState(
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureRecognizer.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureRecognizer.kt
index 5ff583a..74746de 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureRecognizer.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureRecognizer.kt
@@ -43,7 +43,13 @@
     }
 
     override fun accept(event: MotionEvent) {
-        if (!isThreeFingerTouchpadSwipe(event)) return
+        if (!isMultifingerTouchpadSwipe(event)) return
+        if (!isThreeFingerTouchpadSwipe(event)) {
+            if (event.actionMasked == MotionEvent.ACTION_UP) {
+                gestureStateChangedCallback(GestureState.Error)
+            }
+            return
+        }
         val gestureState = distanceTracker.processEvent(event)
         velocityTracker.accept(event)
 
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureHandler.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureHandler.kt
index 21e2917..dd275bd 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureHandler.kt
@@ -40,9 +40,8 @@
         return if (isFromTouchpad && !buttonClick) {
             if (isTwoFingerSwipe(event)) {
                 easterEggGestureMonitor.processTouchpadEvent(event)
-            } else {
-                gestureRecognizer.accept(event)
             }
+            gestureRecognizer.accept(event)
             true
         } else {
             false
diff --git a/packages/SystemUI/src/com/android/systemui/util/EventLogModule.kt b/packages/SystemUI/src/com/android/systemui/util/EventLogModule.kt
index ca0876c..27ada23 100644
--- a/packages/SystemUI/src/com/android/systemui/util/EventLogModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/EventLogModule.kt
@@ -22,5 +22,5 @@
 
 @Module
 interface EventLogModule {
-    @SysUISingleton @Binds fun bindEventLog(eventLogImpl: EventLogImpl?): EventLog?
+    @SysUISingleton @Binds fun bindEventLog(eventLogImpl: EventLogImpl): EventLog
 }
diff --git a/packages/SystemUI/src/com/android/systemui/window/flag/WindowBlurFlag.kt b/packages/SystemUI/src/com/android/systemui/window/flag/WindowBlurFlag.kt
new file mode 100644
index 0000000..8b6c860
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/window/flag/WindowBlurFlag.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.window.flag
+
+import com.android.systemui.Flags
+
+/**
+ * Flag that controls whether the background surface is blurred or not while on the
+ * lockscreen/shade/bouncer. This makes the background of scrim, bouncer and few other opaque
+ * surfaces transparent so that we can see the blur effect on the background surface (wallpaper).
+ */
+object WindowBlurFlag {
+    /** Whether the blur is enabled or not */
+    @JvmStatic
+    val isEnabled
+        // Add flags here that require scrims/background surfaces to be transparent.
+        get() = Flags.notificationShadeBlur() || Flags.bouncerUiRevamp()
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java
index 50d0049..dddaabb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java
@@ -26,10 +26,12 @@
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 
 import android.graphics.PointF;
+
 import android.testing.TestableLooper;
 import android.view.View;
 import android.view.ViewPropertyAnimator;
 import android.view.WindowManager;
+import android.view.accessibility.AccessibilityManager;
 
 import androidx.dynamicanimation.animation.DynamicAnimation;
 import androidx.dynamicanimation.animation.FlingAnimation;
@@ -49,6 +51,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
 
@@ -69,13 +72,17 @@
     @Rule
     public MockitoRule mockito = MockitoJUnit.rule();
 
+    @Mock
+    private AccessibilityManager mAccessibilityManager;
+
     @Before
     public void setUp() throws Exception {
         final WindowManager stubWindowManager = mContext.getSystemService(WindowManager.class);
         final MenuViewAppearance stubMenuViewAppearance = new MenuViewAppearance(mContext,
                 stubWindowManager);
         final SecureSettings secureSettings = TestUtils.mockSecureSettings();
-        final MenuViewModel stubMenuViewModel = new MenuViewModel(mContext, secureSettings);
+        final MenuViewModel stubMenuViewModel = new MenuViewModel(mContext, mAccessibilityManager,
+                secureSettings);
 
         mMenuView = spy(new MenuView(mContext, stubMenuViewModel, stubMenuViewAppearance,
                 secureSettings));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java
index f4580c1..400b3b3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java
@@ -159,7 +159,8 @@
                 new WindowMetrics(mDisplayBounds, fakeDisplayInsets(), /* density = */ 0.0f));
         doReturn(mWindowMetrics).when(mStubWindowManager).getCurrentWindowMetrics();
 
-        mMenuViewModel = new MenuViewModel(mSpyContext, mSecureSettings);
+        mMenuViewModel = new MenuViewModel(
+                mSpyContext, mStubAccessibilityManager, mSecureSettings);
         MenuViewAppearance menuViewAppearance = new MenuViewAppearance(
                 mSpyContext, mStubWindowManager);
         mMenuView = spy(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsFavoritingActivityTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsFavoritingActivityTest.kt
index f5616d4..7fb74b3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsFavoritingActivityTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsFavoritingActivityTest.kt
@@ -51,11 +51,11 @@
 
     private companion object {
         val TEST_COMPONENT = ComponentName("TestPackageName", "TestClassName")
+        val TEST_STRUCTURE: CharSequence = "TestStructure"
         val TEST_CONTROL =
             mock(Control::class.java, Answers.RETURNS_MOCKS)!!.apply {
                 whenever(structure).thenReturn(TEST_STRUCTURE)
             }
-        val TEST_STRUCTURE: CharSequence = "TestStructure"
         val TEST_APP: CharSequence = "TestApp"
 
         private fun View.waitForPost() {
@@ -158,7 +158,7 @@
                 assertThat(getBooleanExtra(ControlsEditingActivity.EXTRA_FROM_FAVORITING, false))
                     .isTrue()
                 assertThat(getCharSequenceExtra(ControlsEditingActivity.EXTRA_STRUCTURE))
-                    .isEqualTo("")
+                    .isEqualTo(TEST_STRUCTURE)
             }
         }
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
index 24bca70..fb70846 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
@@ -41,6 +41,7 @@
 import android.os.UserManager;
 import android.provider.Settings;
 import android.testing.TestableLooper;
+import android.view.Display;
 import android.view.GestureDetector;
 import android.view.IWindowManager;
 import android.view.KeyEvent;
@@ -63,6 +64,7 @@
 import com.android.systemui.animation.DialogTransitionAnimator;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.colorextraction.SysuiColorExtractor;
+import com.android.systemui.display.data.repository.FakeDisplayWindowPropertiesRepository;
 import com.android.systemui.globalactions.domain.interactor.GlobalActionsInteractor;
 import com.android.systemui.kosmos.KosmosJavaAdapter;
 import com.android.systemui.plugins.ActivityStarter;
@@ -159,6 +161,8 @@
                 getContext().getResources().getConfiguration());
         when(mStatusBarWindowControllerStore.getDefaultDisplay())
                 .thenReturn(mStatusBarWindowController);
+        when(mStatusBarWindowControllerStore.forDisplay(anyInt()))
+                .thenReturn(mStatusBarWindowController);
         mGlobalSettings = new FakeGlobalSettings();
         mSecureSettings = new FakeSettings();
         mInteractor = mKosmos.getGlobalActionsInteractor();
@@ -198,7 +202,9 @@
                 mDialogTransitionAnimator,
                 mSelectedUserInteractor,
                 mLogoutInteractor,
-                mInteractor);
+                mInteractor,
+                () -> new FakeDisplayWindowPropertiesRepository(mContext)
+        );
         mGlobalActionsDialogLite.setZeroDialogPressDelayForTesting();
 
         ColorExtractor.GradientColors backdropColors = new ColorExtractor.GradientColors();
@@ -609,13 +615,15 @@
 
         // When entering power menu from lockscreen, with smart lock enabled
         when(mKeyguardUpdateMonitor.getUserHasTrust(anyInt())).thenReturn(true);
-        mGlobalActionsDialogLite.showOrHideDialog(true, true, null /* view */);
+        mGlobalActionsDialogLite.showOrHideDialog(true, true, null /* view */,
+                Display.DEFAULT_DISPLAY);
 
         // Then smart lock will be disabled
         verify(mLockPatternUtils).requireCredentialEntry(eq(expectedUser));
 
         // hide dialog again
-        mGlobalActionsDialogLite.showOrHideDialog(true, true, null /* view */);
+        mGlobalActionsDialogLite.showOrHideDialog(true, true, null /* view */,
+                Display.DEFAULT_DISPLAY);
     }
 
     @Test
@@ -729,13 +737,13 @@
         doReturn(actions).when(mGlobalActionsDialogLite).getDefaultActions();
 
         // Show dialog with keyguard showing
-        mGlobalActionsDialogLite.showOrHideDialog(true, true, null);
+        mGlobalActionsDialogLite.showOrHideDialog(true, true, null, Display.DEFAULT_DISPLAY);
 
         assertOneItemOfType(mGlobalActionsDialogLite.mItems,
                 GlobalActionsDialogLite.SystemUpdateAction.class);
 
         // Hide dialog
-        mGlobalActionsDialogLite.showOrHideDialog(true, true, null);
+        mGlobalActionsDialogLite.showOrHideDialog(true, true, null, Display.DEFAULT_DISPLAY);
     }
 
     @Test
@@ -754,13 +762,13 @@
         doReturn(actions).when(mGlobalActionsDialogLite).getDefaultActions();
 
         // Show dialog with keyguard showing
-        mGlobalActionsDialogLite.showOrHideDialog(false, false, null);
+        mGlobalActionsDialogLite.showOrHideDialog(false, false, null, Display.DEFAULT_DISPLAY);
 
         assertNoItemsOfType(mGlobalActionsDialogLite.mItems,
                 GlobalActionsDialogLite.SystemUpdateAction.class);
 
         // Hide dialog
-        mGlobalActionsDialogLite.showOrHideDialog(false, false, null);
+        mGlobalActionsDialogLite.showOrHideDialog(false, false, null, Display.DEFAULT_DISPLAY);
     }
 
     private UserInfo mockCurrentUser(int flags) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt
index cea51a8..222a7fe 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt
@@ -23,7 +23,6 @@
 import androidx.constraintlayout.widget.ConstraintSet
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.systemui.Flags as AConfigFlags
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.biometrics.AuthController
 import com.android.systemui.flags.FakeFeatureFlags
@@ -66,8 +65,6 @@
     fun setup() {
         MockitoAnnotations.initMocks(this)
 
-        mSetFlagsRule.enableFlags(AConfigFlags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR)
-
         featureFlags =
             FakeFeatureFlagsClassic().apply { set(Flags.LOCKSCREEN_ENABLE_LANDSCAPE, false) }
         underTest =
@@ -88,16 +85,7 @@
     }
 
     @Test
-    fun addViewsConditionally_migrateFlagOn() {
-        mSetFlagsRule.enableFlags(AConfigFlags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR)
-        val constraintLayout = ConstraintLayout(context, null)
-        underTest.addViews(constraintLayout)
-        assertThat(constraintLayout.childCount).isGreaterThan(0)
-    }
-
-    @Test
-    fun addViewsConditionally_migrateAndRefactorFlagsOn() {
-        mSetFlagsRule.enableFlags(AConfigFlags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR)
+    fun addViewsConditionally() {
         val constraintLayout = ConstraintLayout(context, null)
         underTest.addViews(constraintLayout)
         assertThat(constraintLayout.childCount).isGreaterThan(0)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
deleted file mode 100644
index 7e85dd5..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
+++ /dev/null
@@ -1,771 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.keyguard.ui.viewmodel
-
-import android.app.admin.DevicePolicyManager
-import android.content.Intent
-import android.os.UserHandle
-import android.platform.test.flag.junit.FlagsParameterization
-import androidx.test.filters.SmallTest
-import com.android.internal.logging.testing.UiEventLoggerFake
-import com.android.internal.widget.LockPatternUtils
-import com.android.keyguard.logging.KeyguardQuickAffordancesLogger
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.animation.DialogTransitionAnimator
-import com.android.systemui.animation.Expandable
-import com.android.systemui.broadcast.BroadcastDispatcher
-import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.deviceentry.domain.interactor.deviceEntryFaceAuthInteractor
-import com.android.systemui.dock.DockManagerFake
-import com.android.systemui.doze.util.BurnInHelperWrapper
-import com.android.systemui.flags.FakeFeatureFlags
-import com.android.systemui.flags.Flags
-import com.android.systemui.flags.andSceneContainer
-import com.android.systemui.keyguard.data.quickaffordance.BuiltInKeyguardQuickAffordanceKeys
-import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceConfig
-import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceProviderClientFactory
-import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig
-import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLegacySettingSyncer
-import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLocalUserSelectionManager
-import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceRemoteUserSelectionManager
-import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
-import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
-import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor
-import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
-import com.android.systemui.keyguard.domain.interactor.KeyguardQuickAffordanceInteractor
-import com.android.systemui.keyguard.domain.interactor.KeyguardTouchHandlingInteractor
-import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
-import com.android.systemui.keyguard.shared.quickaffordance.ActivationState
-import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition
-import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancesMetricsLogger
-import com.android.systemui.kosmos.testDispatcher
-import com.android.systemui.kosmos.testScope
-import com.android.systemui.plugins.ActivityStarter
-import com.android.systemui.res.R
-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.domain.interactor.shadeInteractor
-import com.android.systemui.shade.pulsingGestureListener
-import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
-import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper
-import com.android.systemui.statusbar.policy.KeyguardStateController
-import com.android.systemui.testKosmos
-import com.android.systemui.util.FakeSharedPreferences
-import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.mock
-import com.android.systemui.util.mockito.whenever
-import com.android.systemui.util.settings.fakeSettings
-import com.google.common.truth.Truth.assertThat
-import kotlin.math.max
-import kotlin.math.min
-import kotlinx.coroutines.flow.map
-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.ArgumentMatchers.anyString
-import org.mockito.Mock
-import org.mockito.Mockito
-import org.mockito.Mockito.verifyNoMoreInteractions
-import org.mockito.MockitoAnnotations
-import platform.test.runner.parameterized.ParameterizedAndroidJunit4
-import platform.test.runner.parameterized.Parameters
-
-@SmallTest
-@RunWith(ParameterizedAndroidJunit4::class)
-class KeyguardBottomAreaViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
-
-    private val kosmos = testKosmos()
-    private val testDispatcher = kosmos.testDispatcher
-    private val testScope = kosmos.testScope
-    private val settings = kosmos.fakeSettings
-
-    @Mock private lateinit var expandable: Expandable
-    @Mock private lateinit var burnInHelperWrapper: BurnInHelperWrapper
-    @Mock private lateinit var lockPatternUtils: LockPatternUtils
-    @Mock private lateinit var keyguardStateController: KeyguardStateController
-    @Mock private lateinit var userTracker: UserTracker
-    @Mock private lateinit var activityStarter: ActivityStarter
-    @Mock private lateinit var launchAnimator: DialogTransitionAnimator
-    @Mock private lateinit var devicePolicyManager: DevicePolicyManager
-    @Mock private lateinit var logger: KeyguardQuickAffordancesLogger
-    @Mock private lateinit var metricsLogger: KeyguardQuickAffordancesMetricsLogger
-    @Mock private lateinit var broadcastDispatcher: BroadcastDispatcher
-    @Mock private lateinit var accessibilityManager: AccessibilityManagerWrapper
-
-    private lateinit var underTest: KeyguardBottomAreaViewModel
-
-    private lateinit var repository: FakeKeyguardRepository
-    private lateinit var homeControlsQuickAffordanceConfig: FakeKeyguardQuickAffordanceConfig
-    private lateinit var quickAccessWalletAffordanceConfig: FakeKeyguardQuickAffordanceConfig
-    private lateinit var qrCodeScannerAffordanceConfig: FakeKeyguardQuickAffordanceConfig
-    private lateinit var dockManager: DockManagerFake
-    private lateinit var biometricSettingsRepository: FakeBiometricSettingsRepository
-
-    init {
-        mSetFlagsRule.setFlagsParameterization(flags)
-    }
-
-    @Before
-    fun setUp() {
-        MockitoAnnotations.initMocks(this)
-
-        overrideResource(R.bool.custom_lockscreen_shortcuts_enabled, true)
-        overrideResource(
-            R.array.config_keyguardQuickAffordanceDefaults,
-            arrayOf(
-                KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START +
-                    ":" +
-                    BuiltInKeyguardQuickAffordanceKeys.HOME_CONTROLS,
-                KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END +
-                    ":" +
-                    BuiltInKeyguardQuickAffordanceKeys.QUICK_ACCESS_WALLET
-            )
-        )
-
-        whenever(burnInHelperWrapper.burnInOffset(anyInt(), any()))
-            .thenReturn(RETURNED_BURN_IN_OFFSET)
-
-        homeControlsQuickAffordanceConfig =
-            FakeKeyguardQuickAffordanceConfig(BuiltInKeyguardQuickAffordanceKeys.HOME_CONTROLS)
-        quickAccessWalletAffordanceConfig =
-            FakeKeyguardQuickAffordanceConfig(
-                BuiltInKeyguardQuickAffordanceKeys.QUICK_ACCESS_WALLET
-            )
-        qrCodeScannerAffordanceConfig =
-            FakeKeyguardQuickAffordanceConfig(BuiltInKeyguardQuickAffordanceKeys.QR_CODE_SCANNER)
-        dockManager = DockManagerFake()
-        biometricSettingsRepository = FakeBiometricSettingsRepository()
-        val featureFlags =
-            FakeFeatureFlags().apply { set(Flags.LOCK_SCREEN_LONG_PRESS_ENABLED, false) }
-
-        val withDeps = KeyguardInteractorFactory.create(featureFlags = featureFlags)
-        val keyguardInteractor = withDeps.keyguardInteractor
-        repository = withDeps.repository
-
-        whenever(userTracker.userHandle).thenReturn(mock())
-        whenever(lockPatternUtils.getStrongAuthForUser(anyInt()))
-            .thenReturn(LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED)
-        val localUserSelectionManager =
-            KeyguardQuickAffordanceLocalUserSelectionManager(
-                context = context,
-                userFileManager =
-                    mock<UserFileManager>().apply {
-                        whenever(
-                                getSharedPreferences(
-                                    anyString(),
-                                    anyInt(),
-                                    anyInt(),
-                                )
-                            )
-                            .thenReturn(FakeSharedPreferences())
-                    },
-                userTracker = userTracker,
-                broadcastDispatcher = fakeBroadcastDispatcher,
-            )
-        val remoteUserSelectionManager =
-            KeyguardQuickAffordanceRemoteUserSelectionManager(
-                scope = testScope.backgroundScope,
-                userTracker = userTracker,
-                clientFactory = FakeKeyguardQuickAffordanceProviderClientFactory(userTracker),
-                userHandle = UserHandle.SYSTEM,
-            )
-        val quickAffordanceRepository =
-            KeyguardQuickAffordanceRepository(
-                appContext = context,
-                scope = testScope.backgroundScope,
-                localUserSelectionManager = localUserSelectionManager,
-                remoteUserSelectionManager = remoteUserSelectionManager,
-                userTracker = userTracker,
-                legacySettingSyncer =
-                    KeyguardQuickAffordanceLegacySettingSyncer(
-                        scope = testScope.backgroundScope,
-                        backgroundDispatcher = testDispatcher,
-                        secureSettings = settings,
-                        selectionsManager = localUserSelectionManager,
-                    ),
-                configs =
-                    setOf(
-                        homeControlsQuickAffordanceConfig,
-                        quickAccessWalletAffordanceConfig,
-                        qrCodeScannerAffordanceConfig,
-                    ),
-                dumpManager = mock(),
-                userHandle = UserHandle.SYSTEM,
-            )
-        val keyguardTouchHandlingInteractor =
-            KeyguardTouchHandlingInteractor(
-                context = mContext,
-                scope = testScope.backgroundScope,
-                transitionInteractor = kosmos.keyguardTransitionInteractor,
-                repository = repository,
-                logger = UiEventLoggerFake(),
-                featureFlags = featureFlags,
-                broadcastDispatcher = broadcastDispatcher,
-                accessibilityManager = accessibilityManager,
-                pulsingGestureListener = kosmos.pulsingGestureListener,
-                faceAuthInteractor = kosmos.deviceEntryFaceAuthInteractor,
-            )
-        underTest =
-            KeyguardBottomAreaViewModel(
-                keyguardInteractor = keyguardInteractor,
-                quickAffordanceInteractor =
-                    KeyguardQuickAffordanceInteractor(
-                        keyguardInteractor = keyguardInteractor,
-                        shadeInteractor = kosmos.shadeInteractor,
-                        lockPatternUtils = lockPatternUtils,
-                        keyguardStateController = keyguardStateController,
-                        userTracker = userTracker,
-                        activityStarter = activityStarter,
-                        featureFlags = featureFlags,
-                        repository = { quickAffordanceRepository },
-                        launchAnimator = launchAnimator,
-                        logger = logger,
-                        metricsLogger = metricsLogger,
-                        devicePolicyManager = devicePolicyManager,
-                        dockManager = dockManager,
-                        biometricSettingsRepository = biometricSettingsRepository,
-                        backgroundDispatcher = testDispatcher,
-                        appContext = mContext,
-                        sceneInteractor = { kosmos.sceneInteractor },
-                    ),
-                bottomAreaInteractor = KeyguardBottomAreaInteractor(repository = repository),
-                burnInHelperWrapper = burnInHelperWrapper,
-                keyguardTouchHandlingViewModel =
-                    KeyguardTouchHandlingViewModel(
-                        interactor = keyguardTouchHandlingInteractor,
-                    ),
-                settingsMenuViewModel =
-                    KeyguardSettingsMenuViewModel(
-                        interactor = keyguardTouchHandlingInteractor,
-                    ),
-            )
-    }
-
-    @Test
-    fun startButton_present_visibleModel_startsActivityOnClick() =
-        testScope.runTest {
-            repository.setKeyguardShowing(true)
-            val latest = collectLastValue(underTest.startButton)
-
-            val testConfig =
-                TestConfig(
-                    isVisible = true,
-                    isClickable = true,
-                    isActivated = true,
-                    icon = mock(),
-                    canShowWhileLocked = false,
-                    intent = Intent("action"),
-                    slotId = KeyguardQuickAffordancePosition.BOTTOM_START.toSlotId(),
-                )
-            val configKey =
-                setUpQuickAffordanceModel(
-                    position = KeyguardQuickAffordancePosition.BOTTOM_START,
-                    testConfig = testConfig,
-                )
-
-            assertQuickAffordanceViewModel(
-                viewModel = latest(),
-                testConfig = testConfig,
-                configKey = configKey,
-            )
-        }
-
-    @Test
-    fun startButton_hiddenWhenDevicePolicyDisablesAllKeyguardFeatures() =
-        testScope.runTest {
-            whenever(devicePolicyManager.getKeyguardDisabledFeatures(null, userTracker.userId))
-                .thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_ALL)
-            repository.setKeyguardShowing(true)
-            val latest by collectLastValue(underTest.startButton)
-
-            val testConfig =
-                TestConfig(
-                    isVisible = true,
-                    isClickable = true,
-                    isActivated = true,
-                    icon = mock(),
-                    canShowWhileLocked = false,
-                    intent = Intent("action"),
-                    slotId = KeyguardQuickAffordancePosition.BOTTOM_START.toSlotId(),
-                )
-            val configKey =
-                setUpQuickAffordanceModel(
-                    position = KeyguardQuickAffordancePosition.BOTTOM_START,
-                    testConfig = testConfig,
-                )
-
-            assertQuickAffordanceViewModel(
-                viewModel = latest,
-                testConfig =
-                    TestConfig(
-                        isVisible = false,
-                        slotId = KeyguardQuickAffordancePosition.BOTTOM_START.toSlotId(),
-                    ),
-                configKey = configKey,
-            )
-        }
-
-    @Test
-    fun startButton_inPreviewMode_visibleEvenWhenKeyguardNotShowing() =
-        testScope.runTest {
-            underTest.enablePreviewMode(
-                initiallySelectedSlotId = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START,
-                shouldHighlightSelectedAffordance = true,
-            )
-            repository.setKeyguardShowing(false)
-            val latest = collectLastValue(underTest.startButton)
-
-            val icon: Icon = mock()
-            val configKey =
-                setUpQuickAffordanceModel(
-                    position = KeyguardQuickAffordancePosition.BOTTOM_START,
-                    testConfig =
-                        TestConfig(
-                            isVisible = true,
-                            isClickable = true,
-                            isActivated = true,
-                            icon = icon,
-                            canShowWhileLocked = false,
-                            intent = Intent("action"),
-                            slotId = KeyguardQuickAffordancePosition.BOTTOM_START.toSlotId(),
-                        ),
-                )
-
-            assertQuickAffordanceViewModel(
-                viewModel = latest(),
-                testConfig =
-                    TestConfig(
-                        isVisible = true,
-                        isClickable = false,
-                        isActivated = false,
-                        icon = icon,
-                        canShowWhileLocked = false,
-                        intent = Intent("action"),
-                        isSelected = true,
-                        slotId = KeyguardQuickAffordancePosition.BOTTOM_START.toSlotId(),
-                    ),
-                configKey = configKey,
-            )
-            assertThat(latest()?.isSelected).isTrue()
-        }
-
-    @Test
-    fun endButton_inHiglightedPreviewMode_dimmedWhenOtherIsSelected() =
-        testScope.runTest {
-            underTest.enablePreviewMode(
-                initiallySelectedSlotId = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START,
-                shouldHighlightSelectedAffordance = true,
-            )
-            repository.setKeyguardShowing(false)
-            val startButton = collectLastValue(underTest.startButton)
-            val endButton = collectLastValue(underTest.endButton)
-
-            val icon: Icon = mock()
-            setUpQuickAffordanceModel(
-                position = KeyguardQuickAffordancePosition.BOTTOM_START,
-                testConfig =
-                    TestConfig(
-                        isVisible = true,
-                        isClickable = true,
-                        isActivated = true,
-                        icon = icon,
-                        canShowWhileLocked = false,
-                        intent = Intent("action"),
-                        slotId = KeyguardQuickAffordancePosition.BOTTOM_START.toSlotId(),
-                    ),
-            )
-            val configKey =
-                setUpQuickAffordanceModel(
-                    position = KeyguardQuickAffordancePosition.BOTTOM_END,
-                    testConfig =
-                        TestConfig(
-                            isVisible = true,
-                            isClickable = true,
-                            isActivated = true,
-                            icon = icon,
-                            canShowWhileLocked = false,
-                            intent = Intent("action"),
-                            slotId = KeyguardQuickAffordancePosition.BOTTOM_END.toSlotId(),
-                        ),
-                )
-
-            assertQuickAffordanceViewModel(
-                viewModel = endButton(),
-                testConfig =
-                    TestConfig(
-                        isVisible = true,
-                        isClickable = false,
-                        isActivated = false,
-                        icon = icon,
-                        canShowWhileLocked = false,
-                        intent = Intent("action"),
-                        isDimmed = true,
-                        slotId = KeyguardQuickAffordancePosition.BOTTOM_END.toSlotId(),
-                    ),
-                configKey = configKey,
-            )
-        }
-
-    @Test
-    fun endButton_present_visibleModel_doNothingOnClick() =
-        testScope.runTest {
-            repository.setKeyguardShowing(true)
-            val latest = collectLastValue(underTest.endButton)
-
-            val config =
-                TestConfig(
-                    isVisible = true,
-                    isClickable = true,
-                    icon = mock(),
-                    canShowWhileLocked = false,
-                    intent =
-                        null, // This will cause it to tell the system that the click was handled.
-                    slotId = KeyguardQuickAffordancePosition.BOTTOM_END.toSlotId(),
-                )
-            val configKey =
-                setUpQuickAffordanceModel(
-                    position = KeyguardQuickAffordancePosition.BOTTOM_END,
-                    testConfig = config,
-                )
-
-            assertQuickAffordanceViewModel(
-                viewModel = latest(),
-                testConfig = config,
-                configKey = configKey,
-            )
-        }
-
-    @Test
-    fun startButton_notPresent_modelIsHidden() =
-        testScope.runTest {
-            val latest = collectLastValue(underTest.startButton)
-
-            val config =
-                TestConfig(
-                    isVisible = false,
-                    slotId = KeyguardQuickAffordancePosition.BOTTOM_START.toSlotId(),
-                )
-            val configKey =
-                setUpQuickAffordanceModel(
-                    position = KeyguardQuickAffordancePosition.BOTTOM_START,
-                    testConfig = config,
-                )
-
-            assertQuickAffordanceViewModel(
-                viewModel = latest(),
-                testConfig = config,
-                configKey = configKey,
-            )
-        }
-
-    @Test
-    fun animateButtonReveal() =
-        testScope.runTest {
-            repository.setKeyguardShowing(true)
-            val testConfig =
-                TestConfig(
-                    isVisible = true,
-                    isClickable = true,
-                    icon = mock(),
-                    canShowWhileLocked = false,
-                    intent = Intent("action"),
-                    slotId = KeyguardQuickAffordancePosition.BOTTOM_START.toSlotId(),
-                )
-
-            setUpQuickAffordanceModel(
-                position = KeyguardQuickAffordancePosition.BOTTOM_START,
-                testConfig = testConfig,
-            )
-
-            val value = collectLastValue(underTest.startButton.map { it.animateReveal })
-
-            assertThat(value()).isFalse()
-            repository.setAnimateDozingTransitions(true)
-            assertThat(value()).isTrue()
-            repository.setAnimateDozingTransitions(false)
-            assertThat(value()).isFalse()
-        }
-
-    @Test
-    fun isOverlayContainerVisible() =
-        testScope.runTest {
-            val value = collectLastValue(underTest.isOverlayContainerVisible)
-
-            assertThat(value()).isTrue()
-            repository.setIsDozing(true)
-            assertThat(value()).isFalse()
-            repository.setIsDozing(false)
-            assertThat(value()).isTrue()
-        }
-
-    @Test
-    fun alpha() =
-        testScope.runTest {
-            val value = collectLastValue(underTest.alpha)
-
-            assertThat(value()).isEqualTo(1f)
-            repository.setBottomAreaAlpha(0.1f)
-            assertThat(value()).isEqualTo(0.1f)
-            repository.setBottomAreaAlpha(0.5f)
-            assertThat(value()).isEqualTo(0.5f)
-            repository.setBottomAreaAlpha(0.2f)
-            assertThat(value()).isEqualTo(0.2f)
-            repository.setBottomAreaAlpha(0f)
-            assertThat(value()).isEqualTo(0f)
-        }
-
-    @Test
-    fun alpha_inPreviewMode_doesNotChange() =
-        testScope.runTest {
-            underTest.enablePreviewMode(
-                initiallySelectedSlotId = null,
-                shouldHighlightSelectedAffordance = false,
-            )
-            val value = collectLastValue(underTest.alpha)
-
-            assertThat(value()).isEqualTo(1f)
-            repository.setBottomAreaAlpha(0.1f)
-            assertThat(value()).isEqualTo(1f)
-            repository.setBottomAreaAlpha(0.5f)
-            assertThat(value()).isEqualTo(1f)
-            repository.setBottomAreaAlpha(0.2f)
-            assertThat(value()).isEqualTo(1f)
-            repository.setBottomAreaAlpha(0f)
-            assertThat(value()).isEqualTo(1f)
-        }
-
-    @Test
-    fun isClickable_trueWhenAlphaAtThreshold() =
-        testScope.runTest {
-            repository.setKeyguardShowing(true)
-            repository.setBottomAreaAlpha(
-                KeyguardBottomAreaViewModel.AFFORDANCE_FULLY_OPAQUE_ALPHA_THRESHOLD
-            )
-
-            val testConfig =
-                TestConfig(
-                    isVisible = true,
-                    isClickable = true,
-                    icon = mock(),
-                    canShowWhileLocked = false,
-                    intent = Intent("action"),
-                    slotId = KeyguardQuickAffordancePosition.BOTTOM_START.toSlotId(),
-                )
-            val configKey =
-                setUpQuickAffordanceModel(
-                    position = KeyguardQuickAffordancePosition.BOTTOM_START,
-                    testConfig = testConfig,
-                )
-
-            val latest = collectLastValue(underTest.startButton)
-
-            assertQuickAffordanceViewModel(
-                viewModel = latest(),
-                testConfig = testConfig,
-                configKey = configKey,
-            )
-        }
-
-    @Test
-    fun isClickable_trueWhenAlphaAboveThreshold() =
-        testScope.runTest {
-            repository.setKeyguardShowing(true)
-            val latest = collectLastValue(underTest.startButton)
-            repository.setBottomAreaAlpha(
-                min(1f, KeyguardBottomAreaViewModel.AFFORDANCE_FULLY_OPAQUE_ALPHA_THRESHOLD + 0.1f),
-            )
-
-            val testConfig =
-                TestConfig(
-                    isVisible = true,
-                    isClickable = true,
-                    icon = mock(),
-                    canShowWhileLocked = false,
-                    intent = Intent("action"),
-                    slotId = KeyguardQuickAffordancePosition.BOTTOM_START.toSlotId(),
-                )
-            val configKey =
-                setUpQuickAffordanceModel(
-                    position = KeyguardQuickAffordancePosition.BOTTOM_START,
-                    testConfig = testConfig,
-                )
-
-            assertQuickAffordanceViewModel(
-                viewModel = latest(),
-                testConfig = testConfig,
-                configKey = configKey,
-            )
-        }
-
-    @Test
-    fun isClickable_falseWhenAlphaBelowThreshold() =
-        testScope.runTest {
-            repository.setKeyguardShowing(true)
-            val latest = collectLastValue(underTest.startButton)
-            repository.setBottomAreaAlpha(
-                max(0f, KeyguardBottomAreaViewModel.AFFORDANCE_FULLY_OPAQUE_ALPHA_THRESHOLD - 0.1f),
-            )
-
-            val testConfig =
-                TestConfig(
-                    isVisible = true,
-                    isClickable = false,
-                    icon = mock(),
-                    canShowWhileLocked = false,
-                    intent = Intent("action"),
-                    slotId = KeyguardQuickAffordancePosition.BOTTOM_START.toSlotId(),
-                )
-            val configKey =
-                setUpQuickAffordanceModel(
-                    position = KeyguardQuickAffordancePosition.BOTTOM_START,
-                    testConfig = testConfig,
-                )
-
-            assertQuickAffordanceViewModel(
-                viewModel = latest(),
-                testConfig = testConfig,
-                configKey = configKey,
-            )
-        }
-
-    @Test
-    fun isClickable_falseWhenAlphaAtZero() =
-        testScope.runTest {
-            repository.setKeyguardShowing(true)
-            val latest = collectLastValue(underTest.startButton)
-            repository.setBottomAreaAlpha(0f)
-
-            val testConfig =
-                TestConfig(
-                    isVisible = true,
-                    isClickable = false,
-                    icon = mock(),
-                    canShowWhileLocked = false,
-                    intent = Intent("action"),
-                    slotId = KeyguardQuickAffordancePosition.BOTTOM_START.toSlotId(),
-                )
-            val configKey =
-                setUpQuickAffordanceModel(
-                    position = KeyguardQuickAffordancePosition.BOTTOM_START,
-                    testConfig = testConfig,
-                )
-
-            assertQuickAffordanceViewModel(
-                viewModel = latest(),
-                testConfig = testConfig,
-                configKey = configKey,
-            )
-        }
-
-    private suspend fun setUpQuickAffordanceModel(
-        position: KeyguardQuickAffordancePosition,
-        testConfig: TestConfig,
-    ): String {
-        val config =
-            when (position) {
-                KeyguardQuickAffordancePosition.BOTTOM_START -> homeControlsQuickAffordanceConfig
-                KeyguardQuickAffordancePosition.BOTTOM_END -> quickAccessWalletAffordanceConfig
-            }
-
-        val lockScreenState =
-            if (testConfig.isVisible) {
-                if (testConfig.intent != null) {
-                    config.onTriggeredResult =
-                        KeyguardQuickAffordanceConfig.OnTriggeredResult.StartActivity(
-                            intent = testConfig.intent,
-                            canShowWhileLocked = testConfig.canShowWhileLocked,
-                        )
-                }
-                KeyguardQuickAffordanceConfig.LockScreenState.Visible(
-                    icon = testConfig.icon ?: error("Icon is unexpectedly null!"),
-                    activationState =
-                        when (testConfig.isActivated) {
-                            true -> ActivationState.Active
-                            false -> ActivationState.Inactive
-                        }
-                )
-            } else {
-                KeyguardQuickAffordanceConfig.LockScreenState.Hidden
-            }
-        config.setState(lockScreenState)
-
-        return "${position.toSlotId()}::${config.key}"
-    }
-
-    private fun assertQuickAffordanceViewModel(
-        viewModel: KeyguardQuickAffordanceViewModel?,
-        testConfig: TestConfig,
-        configKey: String,
-    ) {
-        checkNotNull(viewModel)
-        assertThat(viewModel.isVisible).isEqualTo(testConfig.isVisible)
-        assertThat(viewModel.isClickable).isEqualTo(testConfig.isClickable)
-        assertThat(viewModel.isActivated).isEqualTo(testConfig.isActivated)
-        assertThat(viewModel.isSelected).isEqualTo(testConfig.isSelected)
-        assertThat(viewModel.isDimmed).isEqualTo(testConfig.isDimmed)
-        assertThat(viewModel.slotId).isEqualTo(testConfig.slotId)
-        if (testConfig.isVisible) {
-            assertThat(viewModel.icon).isEqualTo(testConfig.icon)
-            viewModel.onClicked.invoke(
-                KeyguardQuickAffordanceViewModel.OnClickedParameters(
-                    configKey = configKey,
-                    expandable = expandable,
-                    slotId = viewModel.slotId,
-                )
-            )
-            if (testConfig.intent != null) {
-                assertThat(Mockito.mockingDetails(activityStarter).invocations).hasSize(1)
-            } else {
-                verifyNoMoreInteractions(activityStarter)
-            }
-        } else {
-            assertThat(viewModel.isVisible).isFalse()
-        }
-    }
-
-    private data class TestConfig(
-        val isVisible: Boolean,
-        val isClickable: Boolean = false,
-        val isActivated: Boolean = false,
-        val icon: Icon? = null,
-        val canShowWhileLocked: Boolean = false,
-        val intent: Intent? = null,
-        val isSelected: Boolean = false,
-        val isDimmed: Boolean = false,
-        val slotId: String = ""
-    ) {
-        init {
-            check(!isVisible || icon != null) { "Must supply non-null icon if visible!" }
-        }
-    }
-
-    companion object {
-        private const val DEFAULT_BURN_IN_OFFSET = 5
-        private const val RETURNED_BURN_IN_OFFSET = 3
-
-        @JvmStatic
-        @Parameters(name = "{0}")
-        fun getParams(): List<FlagsParameterization> {
-            return FlagsParameterization.allCombinationsOf().andSceneContainer()
-        }
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt
index cb2c8fc..3364528 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt
@@ -25,7 +25,6 @@
 import androidx.test.filters.SmallTest
 import com.android.internal.widget.LockPatternUtils
 import com.android.keyguard.logging.KeyguardQuickAffordancesLogger
-import com.android.systemui.Flags as AConfigFlags
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.animation.DialogTransitionAnimator
 import com.android.systemui.animation.Expandable
@@ -191,8 +190,6 @@
         dockManager = DockManagerFake()
         biometricSettingsRepository = FakeBiometricSettingsRepository()
 
-        mSetFlagsRule.enableFlags(AConfigFlags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR)
-
         val featureFlags =
             FakeFeatureFlags().apply { set(Flags.LOCK_SCREEN_LONG_PRESS_ENABLED, false) }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/lifecycle/RepeatWhenAttachedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/lifecycle/RepeatWhenAttachedTest.kt
index d3409c7..e3aeaa8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/lifecycle/RepeatWhenAttachedTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/lifecycle/RepeatWhenAttachedTest.kt
@@ -484,14 +484,12 @@
     private fun CoroutineScope.repeatWhenAttached(): DisposableHandle {
         return view.repeatWhenAttached(
             coroutineContext = coroutineContext,
-            block = block,
+            block = { block.invoke(this) },
         )
     }
 
-    private class Block : suspend LifecycleOwner.(View) -> Unit {
-        data class Invocation(
-            val lifecycleOwner: LifecycleOwner,
-        ) {
+    private class Block {
+        data class Invocation(val lifecycleOwner: LifecycleOwner) {
             val lifecycleState: Lifecycle.State
                 get() = lifecycleOwner.lifecycle.currentState
         }
@@ -504,7 +502,7 @@
         val latestLifecycleState: Lifecycle.State
             get() = _invocations.last().lifecycleState
 
-        override suspend fun invoke(lifecycleOwner: LifecycleOwner, view: View) {
+        fun invoke(lifecycleOwner: LifecycleOwner) {
             _invocations.add(Invocation(lifecycleOwner))
         }
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaControlPanelTest.kt
index 68a5d93..9543032 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaControlPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaControlPanelTest.kt
@@ -247,7 +247,7 @@
             mContext,
             0,
             intent.setPackage(mContext.packageName),
-            PendingIntent.FLAG_MUTABLE
+            PendingIntent.FLAG_MUTABLE,
         )
 
     @JvmField @Rule val mockito = MockitoJUnit.rule()
@@ -294,7 +294,7 @@
                 override fun loadAnimator(
                     animId: Int,
                     otionInterpolator: Interpolator,
-                    vararg targets: View
+                    vararg targets: View,
                 ): AnimatorSet {
                     return mockAnimator
                 }
@@ -323,7 +323,7 @@
                 packageName = PACKAGE,
                 instanceId = instanceId,
                 recommendations = listOf(smartspaceAction, smartspaceAction, smartspaceAction),
-                cardAction = smartspaceAction
+                cardAction = smartspaceAction,
             )
     }
 
@@ -370,7 +370,7 @@
                 packageName = PACKAGE,
                 token = session.sessionToken,
                 device = device,
-                instanceId = instanceId
+                instanceId = instanceId,
             )
     }
 
@@ -416,7 +416,7 @@
                         action1.id,
                         action2.id,
                         action3.id,
-                        action4.id
+                        action4.id,
                     )
             }
 
@@ -536,7 +536,7 @@
                 playOrPause = MediaAction(icon, Runnable {}, "play", bg),
                 nextOrCustom = MediaAction(icon, Runnable {}, "next", bg),
                 custom0 = MediaAction(icon, null, "custom 0", bg),
-                custom1 = MediaAction(icon, null, "custom 1", bg)
+                custom1 = MediaAction(icon, null, "custom 1", bg),
             )
         val state = mediaData.copy(semanticActions = semanticActions)
         player.attachPlayer(viewHolder)
@@ -590,7 +590,7 @@
                 custom0 = MediaAction(icon, null, "custom 0", bg),
                 custom1 = MediaAction(icon, null, "custom 1", bg),
                 false,
-                true
+                true,
             )
         val state = mediaData.copy(semanticActions = semanticActions)
 
@@ -622,7 +622,7 @@
                 custom0 = MediaAction(icon, null, "custom 0", bg),
                 custom1 = MediaAction(icon, null, "custom 1", bg),
                 true,
-                false
+                false,
             )
         val state = mediaData.copy(semanticActions = semanticActions)
 
@@ -760,7 +760,7 @@
         val semanticActions =
             MediaButton(
                 playOrPause = MediaAction(icon, Runnable {}, "play", null),
-                nextOrCustom = MediaAction(icon, Runnable {}, "next", null)
+                nextOrCustom = MediaAction(icon, Runnable {}, "next", null),
             )
         val state = mediaData.copy(semanticActions = semanticActions)
 
@@ -850,7 +850,7 @@
         val semanticActions =
             MediaButton(
                 prevOrCustom = MediaAction(icon, {}, "prev", null),
-                nextOrCustom = MediaAction(icon, {}, "next", null)
+                nextOrCustom = MediaAction(icon, {}, "next", null),
             )
         val state = mediaData.copy(semanticActions = semanticActions)
 
@@ -921,7 +921,7 @@
         val semanticActions =
             MediaButton(
                 prevOrCustom = MediaAction(icon, {}, "prev", null),
-                nextOrCustom = MediaAction(icon, {}, "next", null)
+                nextOrCustom = MediaAction(icon, {}, "next", null),
             )
         val state = mediaData.copy(semanticActions = semanticActions)
         player.attachPlayer(viewHolder)
@@ -944,7 +944,7 @@
         val semanticActions =
             MediaButton(
                 prevOrCustom = MediaAction(icon, {}, "prev", null),
-                nextOrCustom = MediaAction(icon, {}, "next", null)
+                nextOrCustom = MediaAction(icon, {}, "next", null),
             )
         val state = mediaData.copy(semanticActions = semanticActions)
 
@@ -966,6 +966,29 @@
     }
 
     @Test
+    fun setIsScrubbing_reservedButtonSpaces_scrubbingTimesShown() {
+        val semanticActions =
+            MediaButton(
+                prevOrCustom = null,
+                nextOrCustom = null,
+                reserveNext = true,
+                reservePrev = true,
+            )
+        val state = mediaData.copy(semanticActions = semanticActions)
+        player.attachPlayer(viewHolder)
+        player.bindPlayer(state, PACKAGE)
+        reset(expandedSet)
+
+        getScrubbingChangeListener().onScrubbingChanged(true)
+        mainExecutor.runAllReady()
+
+        verify(expandedSet).setVisibility(R.id.actionPrev, View.GONE)
+        verify(expandedSet).setVisibility(R.id.actionNext, View.GONE)
+        verify(expandedSet).setVisibility(R.id.media_scrubbing_elapsed_time, View.VISIBLE)
+        verify(expandedSet).setVisibility(R.id.media_scrubbing_total_time, View.VISIBLE)
+    }
+
+    @Test
     fun bind_resumeState_withProgress() {
         val progress = 0.5
         val state = mediaData.copy(resumption = true, resumeProgress = progress)
@@ -1009,13 +1032,13 @@
                 MediaNotificationAction(true, actionIntent = pendingIntent, icon, "play"),
                 MediaNotificationAction(true, actionIntent = null, icon, "next"),
                 MediaNotificationAction(true, actionIntent = null, icon, "custom 0"),
-                MediaNotificationAction(true, actionIntent = pendingIntent, icon, "custom 1")
+                MediaNotificationAction(true, actionIntent = pendingIntent, icon, "custom 1"),
             )
         val state =
             mediaData.copy(
                 actions = actions,
                 actionsToShowInCompact = listOf(1, 2),
-                semanticActions = null
+                semanticActions = null,
             )
 
         player.attachPlayer(viewHolder)
@@ -1701,7 +1724,7 @@
                 MediaNotificationAction(true, actionIntent = pendingIntent, null, "action 1"),
                 MediaNotificationAction(true, actionIntent = pendingIntent, null, "action 2"),
                 MediaNotificationAction(true, actionIntent = pendingIntent, null, "action 3"),
-                MediaNotificationAction(true, actionIntent = pendingIntent, null, "action 4")
+                MediaNotificationAction(true, actionIntent = pendingIntent, null, "action 4"),
             )
         val data = mediaData.copy(actions = actions)
 
@@ -1720,7 +1743,7 @@
                 MediaNotificationAction(true, actionIntent = pendingIntent, null, "action 1"),
                 MediaNotificationAction(true, actionIntent = pendingIntent, null, "action 2"),
                 MediaNotificationAction(true, actionIntent = pendingIntent, null, "action 3"),
-                MediaNotificationAction(true, actionIntent = pendingIntent, null, "action 4")
+                MediaNotificationAction(true, actionIntent = pendingIntent, null, "action 4"),
             )
         val data = mediaData.copy(actions = actions)
 
@@ -1739,7 +1762,7 @@
                 MediaNotificationAction(true, actionIntent = pendingIntent, null, "action 1"),
                 MediaNotificationAction(true, actionIntent = pendingIntent, null, "action 2"),
                 MediaNotificationAction(true, actionIntent = pendingIntent, null, "action 3"),
-                MediaNotificationAction(true, actionIntent = pendingIntent, null, "action 4")
+                MediaNotificationAction(true, actionIntent = pendingIntent, null, "action 4"),
             )
         val data = mediaData.copy(actions = actions)
 
@@ -2021,7 +2044,7 @@
                             .setSubtitle(subtitle3)
                             .setIcon(icon)
                             .setExtras(Bundle.EMPTY)
-                            .build()
+                            .build(),
                     )
             )
         player.bindRecommendation(data)
@@ -2047,7 +2070,7 @@
                             .setIcon(
                                 Icon.createWithResource(
                                     context,
-                                    com.android.settingslib.R.drawable.ic_1x_mobiledata
+                                    com.android.settingslib.R.drawable.ic_1x_mobiledata,
                                 )
                             )
                             .setExtras(Bundle.EMPTY)
@@ -2084,7 +2107,7 @@
                             .setSubtitle("fake subtitle")
                             .setIcon(icon)
                             .setExtras(Bundle.EMPTY)
-                            .build()
+                            .build(),
                     )
             )
         player.bindRecommendation(data)
@@ -2119,7 +2142,7 @@
                             .setSubtitle("")
                             .setIcon(icon)
                             .setExtras(Bundle.EMPTY)
-                            .build()
+                            .build(),
                     )
             )
         player.bindRecommendation(data)
@@ -2142,7 +2165,7 @@
                             .setIcon(
                                 Icon.createWithResource(
                                     context,
-                                    com.android.settingslib.R.drawable.ic_1x_mobiledata
+                                    com.android.settingslib.R.drawable.ic_1x_mobiledata,
                                 )
                             )
                             .setExtras(Bundle.EMPTY)
@@ -2157,11 +2180,11 @@
                             .setIcon(
                                 Icon.createWithResource(
                                     context,
-                                    com.android.settingslib.R.drawable.ic_3g_mobiledata
+                                    com.android.settingslib.R.drawable.ic_3g_mobiledata,
                                 )
                             )
                             .setExtras(Bundle.EMPTY)
-                            .build()
+                            .build(),
                     )
             )
 
@@ -2185,7 +2208,7 @@
                             .setIcon(
                                 Icon.createWithResource(
                                     context,
-                                    com.android.settingslib.R.drawable.ic_1x_mobiledata
+                                    com.android.settingslib.R.drawable.ic_1x_mobiledata,
                                 )
                             )
                             .setExtras(Bundle.EMPTY)
@@ -2200,11 +2223,11 @@
                             .setIcon(
                                 Icon.createWithResource(
                                     context,
-                                    com.android.settingslib.R.drawable.ic_3g_mobiledata
+                                    com.android.settingslib.R.drawable.ic_3g_mobiledata,
                                 )
                             )
                             .setExtras(Bundle.EMPTY)
-                            .build()
+                            .build(),
                     )
             )
 
@@ -2245,7 +2268,7 @@
                             .setSubtitle("subtitle1")
                             .setIcon(albumArt)
                             .setExtras(Bundle.EMPTY)
-                            .build()
+                            .build(),
                     )
             )
 
@@ -2268,7 +2291,7 @@
             Bundle().apply {
                 putInt(
                     MediaConstants.DESCRIPTION_EXTRAS_KEY_COMPLETION_STATUS,
-                    MediaConstants.DESCRIPTION_EXTRAS_VALUE_COMPLETION_STATUS_PARTIALLY_PLAYED
+                    MediaConstants.DESCRIPTION_EXTRAS_VALUE_COMPLETION_STATUS_PARTIALLY_PLAYED,
                 )
                 putDouble(MediaConstants.DESCRIPTION_EXTRAS_KEY_COMPLETION_PERCENTAGE, 0.5)
             }
@@ -2290,7 +2313,7 @@
                             .setSubtitle("subtitle1")
                             .setIcon(albumArt)
                             .setExtras(Bundle.EMPTY)
-                            .build()
+                            .build(),
                     )
             )
 
@@ -2328,7 +2351,7 @@
                             .setSubtitle("subtitle1")
                             .setIcon(albumArt)
                             .setExtras(Bundle.EMPTY)
-                            .build()
+                            .build(),
                     )
             )
 
@@ -2381,7 +2404,7 @@
                             .setSubtitle("subtitle1")
                             .setIcon(albumArt)
                             .setExtras(Bundle.EMPTY)
-                            .build()
+                            .build(),
                     )
             )
 
@@ -2444,7 +2467,7 @@
                         icon = null,
                         action = {},
                         contentDescription = "play",
-                        background = null
+                        background = null,
                     )
             )
         val data = mediaData.copy(semanticActions = semanticActions)
@@ -2465,7 +2488,7 @@
                         icon = null,
                         action = {},
                         contentDescription = "play",
-                        background = null
+                        background = null,
                     )
             )
         val data = mediaData.copy(semanticActions = semanticActions)
@@ -2498,7 +2521,7 @@
                         icon = null,
                         action = {},
                         contentDescription = "play",
-                        background = null
+                        background = null,
                     )
             )
         val data = mediaData.copy(semanticActions = semanticActions)
@@ -2530,8 +2553,8 @@
                         icon = null,
                         action = {},
                         contentDescription = "custom0",
-                        background = null
-                    ),
+                        background = null,
+                    )
             )
         val data = mediaData.copy(semanticActions = semanticActions)
         player.attachPlayer(viewHolder)
@@ -2553,8 +2576,8 @@
                         icon = null,
                         action = {},
                         contentDescription = "custom0",
-                        background = null
-                    ),
+                        background = null,
+                    )
             )
         val data = mediaData.copy(semanticActions = semanticActions)
         player.attachPlayer(viewHolder)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt
index 90ffaf1..67c5986 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt
@@ -236,7 +236,7 @@
 
         context.orCreateTestableResources.addOverride(
             R.array.tile_states_internet,
-            arrayOf(unavailableString, offString, onString)
+            arrayOf(unavailableString, offString, onString),
         )
 
         // State UNAVAILABLE
@@ -341,7 +341,7 @@
         val testA11yLabel = "TEST_LABEL"
         context.orCreateTestableResources.addOverride(
             R.string.accessibility_tile_disabled_by_policy_action_description,
-            testA11yLabel
+            testA11yLabel,
         )
 
         val stateDisabledByPolicy = QSTile.State()
@@ -374,7 +374,7 @@
 
         context.orCreateTestableResources.addOverride(
             R.array.tile_states_internet,
-            arrayOf(unavailableString, offString, onString)
+            arrayOf(unavailableString, offString, onString),
         )
 
         tileView.changeState(state)
@@ -477,6 +477,24 @@
     }
 
     @Test
+    fun onStateChange_fromLongPress_toNoLongPress_whileLongPressRuns_doesNotClearResources() {
+        // GIVEN that the long-press effect has been initialized
+        val state = QSTile.State()
+        state.handlesLongClick = true
+        tileView.changeState(state)
+
+        // WHEN the long-press effect is running
+        kosmos.qsLongPressEffect.setState(QSLongPressEffect.State.RUNNING_FORWARD)
+
+        // WHEN a state changed happens so that the tile no longer handles long-press
+        state.handlesLongClick = false
+        tileView.changeState(state)
+
+        // THEN the long-press effect resources are not cleared
+        assertThat(tileView.areLongPressEffectPropertiesSet).isTrue()
+    }
+
+    @Test
     fun onStateChange_withoutLongPressEffect_fromLongPress_to_noLongPress_neverSetsProperties() {
         // GIVEN a tile where the long-press effect is null
         tileView = FakeTileView(context, false, null)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateTest.java
index 7b24233..300c9b8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateTest.java
@@ -6,6 +6,7 @@
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
@@ -23,8 +24,8 @@
 import android.widget.Switch;
 import android.widget.TextView;
 
-import androidx.test.annotation.UiThreadTest;
 import androidx.recyclerview.widget.RecyclerView;
+import androidx.test.annotation.UiThreadTest;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
@@ -40,8 +41,6 @@
 import com.android.systemui.util.time.FakeSystemClock;
 import com.android.wifitrackerlib.WifiEntry;
 
-import kotlinx.coroutines.CoroutineScope;
-
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -52,6 +51,8 @@
 
 import java.util.List;
 
+import kotlinx.coroutines.CoroutineScope;
+
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
@@ -127,7 +128,7 @@
                 .spyStatic(WifiEnterpriseRestrictionUtils.class)
                 .startMocking();
         when(WifiEnterpriseRestrictionUtils.isChangeWifiStateAllowed(mContext)).thenReturn(true);
-        when(mSystemUIDialogFactory.create(any(SystemUIDialog.Delegate.class)))
+        when(mSystemUIDialogFactory.create(any(SystemUIDialog.Delegate.class), eq(mContext)))
                 .thenReturn(mSystemUIDialog);
         when(mSystemUIDialog.getContext()).thenReturn(mContext);
         when(mSystemUIDialog.getWindow()).thenReturn(mWindow);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java
index a17f100..afff485 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java
@@ -36,7 +36,6 @@
 import android.app.PendingIntent;
 import android.content.Context;
 import android.content.Intent;
-import android.media.projection.StopReason;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 
@@ -155,7 +154,7 @@
         PendingIntent stopIntent = Mockito.mock(PendingIntent.class);
 
         mController.startCountdown(0, 0, startIntent, stopIntent);
-        mController.stopRecording(StopReason.STOP_UNKNOWN);
+        mController.stopRecording();
 
         assertFalse(mController.isStarting());
         assertFalse(mController.isRecording());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutListSearchTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutListSearchTest.java
index 63e56ee..8045a13 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutListSearchTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutListSearchTest.java
@@ -26,7 +26,6 @@
 
 import android.graphics.drawable.Icon;
 import android.os.Handler;
-import android.platform.test.annotations.EnableFlags;
 import android.view.KeyboardShortcutGroup;
 import android.view.KeyboardShortcutInfo;
 import android.view.WindowManager;
@@ -34,7 +33,6 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
-import com.android.systemui.Flags;
 import com.android.systemui.SysuiTestCase;
 
 import com.google.android.material.bottomsheet.BottomSheetDialog;
@@ -95,7 +93,6 @@
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_VALIDATE_KEYBOARD_SHORTCUT_HELPER_ICON_URI)
     public void requestAppKeyboardShortcuts_callback_sanitisesIcons() {
         KeyboardShortcutGroup group = createKeyboardShortcutGroupForIconTests();
 
@@ -114,7 +111,6 @@
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_VALIDATE_KEYBOARD_SHORTCUT_HELPER_ICON_URI)
     public void requestImeKeyboardShortcuts_callback_sanitisesIcons() {
         KeyboardShortcutGroup group = createKeyboardShortcutGroupForIconTests();
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutsTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutsTest.java
index 105cf16..20ecaf7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutsTest.java
@@ -31,7 +31,6 @@
 import android.app.Dialog;
 import android.graphics.drawable.Icon;
 import android.os.Handler;
-import android.platform.test.annotations.EnableFlags;
 import android.view.KeyboardShortcutGroup;
 import android.view.KeyboardShortcutInfo;
 import android.view.WindowManager;
@@ -39,7 +38,6 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
-import com.android.systemui.Flags;
 import com.android.systemui.SysuiTestCase;
 
 import org.junit.Before;
@@ -131,7 +129,6 @@
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_VALIDATE_KEYBOARD_SHORTCUT_HELPER_ICON_URI)
     public void requestAppKeyboardShortcuts_callback_sanitisesIcons() {
         KeyboardShortcutGroup group = createKeyboardShortcutGroupForIconTests();
         KeyboardShortcuts.toggle(mContext, DEVICE_ID);
@@ -143,7 +140,6 @@
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_VALIDATE_KEYBOARD_SHORTCUT_HELPER_ICON_URI)
     public void requestImeKeyboardShortcuts_callback_sanitisesIcons() {
         KeyboardShortcutGroup group = createKeyboardShortcutGroupForIconTests();
         KeyboardShortcuts.toggle(mContext, DEVICE_ID);
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 99467cb..a91fb45 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
@@ -95,6 +95,7 @@
 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.headsup.AvalancheController;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.ExpandableView;
 import com.android.systemui.statusbar.notification.shared.NotificationThrottleHun;
@@ -103,7 +104,6 @@
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
-import com.android.systemui.statusbar.notification.headsup.AvalancheController;
 import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController;
 import com.android.systemui.wallpapers.domain.interactor.WallpaperInteractor;
 
@@ -356,6 +356,31 @@
 
     @Test
     @EnableSceneContainer
+    public void updateStackCutoff_updatesStackEndHeight() {
+        // GIVEN shade is fully open
+        final float stackTop = 200f;
+        final float stackCutoff = 1000f;
+        final float stackHeight = stackCutoff - stackTop;
+        mAmbientState.setStackTop(stackTop);
+        mAmbientState.setStackCutoff(stackCutoff);
+        mAmbientState.setStatusBarState(StatusBarState.SHADE);
+        mStackScroller.setMaxDisplayedNotifications(-1); // no limit on the shade
+        mStackScroller.setExpandFraction(1f); // shade is fully expanded
+        assertThat(mAmbientState.getStackEndHeight()).isEqualTo(stackHeight);
+        assertThat(mAmbientState.getInterpolatedStackHeight()).isEqualTo(stackHeight);
+
+        // WHEN stackCutoff changes
+        final float newStackCutoff = 800;
+        mStackScroller.setStackCutoff(newStackCutoff);
+
+        // THEN stackEndHeight is updated
+        final float newStackHeight = newStackCutoff - stackTop;
+        assertThat(mAmbientState.getStackEndHeight()).isEqualTo(newStackHeight);
+        assertThat(mAmbientState.getInterpolatedStackHeight()).isEqualTo(newStackHeight);
+    }
+
+    @Test
+    @EnableSceneContainer
     public void updateStackEndHeightAndStackHeight_maxNotificationsSet_withSceneContainer() {
         float stackHeight = 300f;
         when(mStackSizeCalculator.computeHeight(eq(mStackScroller), anyInt(), anyFloat()))
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 abdd797..0ba0aeb 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
@@ -45,6 +45,7 @@
 import com.android.systemui.shade.ShadeLogger
 import com.android.systemui.shade.ShadeViewController
 import com.android.systemui.shade.StatusBarLongPressGestureDetector
+import com.android.systemui.shade.data.repository.fakeShadeDisplaysRepository
 import com.android.systemui.shade.display.StatusBarTouchShadeDisplayPolicy
 import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor
 import com.android.systemui.statusbar.CommandQueue
@@ -77,6 +78,7 @@
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.`when`
 import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.verifyNoMoreInteractions
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
@@ -86,6 +88,7 @@
     private val statusBarContentInsetsProvider = statusBarContentInsetsProviderStore.defaultDisplay
 
     private val fakeDarkIconDispatcher = kosmos.fakeDarkIconDispatcher
+    private val fakeShadeDisplaysRepository = kosmos.fakeShadeDisplaysRepository
     @Mock private lateinit var shadeViewController: ShadeViewController
     @Mock private lateinit var panelExpansionInteractor: PanelExpansionInteractor
     @Mock private lateinit var featureFlags: FeatureFlags
@@ -260,6 +263,64 @@
     }
 
     @Test
+    @DisableFlags(AconfigFlags.FLAG_STATUS_BAR_CONNECTED_DISPLAYS)
+    fun handleTouchEventFromStatusBar_statusBarConnectedDisplaysDisabled_viewReceivesEvent() {
+        `when`(centralSurfacesImpl.commandQueuePanelsEnabled).thenReturn(true)
+        `when`(shadeViewController.isViewEnabled).thenReturn(true)
+        fakeShadeDisplaysRepository.setDisplayId(SECONDARY_DISPLAY_ID)
+        val event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 2f, 0)
+
+        view.onTouchEvent(event)
+
+        verify(shadeViewController).handleExternalTouch(event)
+    }
+
+    @Test
+    @EnableFlags(
+        AconfigFlags.FLAG_STATUS_BAR_CONNECTED_DISPLAYS,
+        AconfigFlags.FLAG_SHADE_WINDOW_GOES_AROUND,
+    )
+    fun handleTouchEventFromStatusBar_statusBarConnectedDisplaysEnabled_shadeWindowGoesAroundEnabled_viewReceivesEvent() {
+        `when`(centralSurfacesImpl.commandQueuePanelsEnabled).thenReturn(true)
+        `when`(shadeViewController.isViewEnabled).thenReturn(true)
+        fakeShadeDisplaysRepository.setDisplayId(SECONDARY_DISPLAY_ID)
+        val event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 2f, 0)
+
+        view.onTouchEvent(event)
+
+        verify(shadeViewController).handleExternalTouch(event)
+    }
+
+    @Test
+    @EnableFlags(AconfigFlags.FLAG_STATUS_BAR_CONNECTED_DISPLAYS)
+    @DisableFlags(AconfigFlags.FLAG_SHADE_WINDOW_GOES_AROUND)
+    fun handleTouchEventFromStatusBar_touchOnShadeDisplay_statusBarConnectedDisplaysEnabled_shadeWindowGoesAroundDisabled_viewReceivesEvent() {
+        `when`(centralSurfacesImpl.commandQueuePanelsEnabled).thenReturn(true)
+        `when`(shadeViewController.isViewEnabled).thenReturn(true)
+        fakeShadeDisplaysRepository.setDisplayId(DISPLAY_ID)
+        val event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 2f, 0)
+
+        view.onTouchEvent(event)
+
+        verify(shadeViewController).handleExternalTouch(event)
+    }
+
+    @Test
+    @EnableFlags(AconfigFlags.FLAG_STATUS_BAR_CONNECTED_DISPLAYS)
+    @DisableFlags(AconfigFlags.FLAG_SHADE_WINDOW_GOES_AROUND)
+    fun handleTouchEventFromStatusBar_touchNotOnShadeDisplay_statusBarConnectedDisplaysEnabled_shadeWindowGoesAroundDisabled_viewDoesNotReceiveEvent() {
+        `when`(centralSurfacesImpl.commandQueuePanelsEnabled).thenReturn(true)
+        `when`(shadeViewController.isViewEnabled).thenReturn(true)
+        fakeShadeDisplaysRepository.setDisplayId(SECONDARY_DISPLAY_ID)
+        val event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 2f, 0)
+
+        view.onTouchEvent(event)
+
+        verify(shadeViewController).isViewEnabled
+        verifyNoMoreInteractions(shadeViewController)
+    }
+
+    @Test
     @DisableFlags(com.android.systemui.Flags.FLAG_STATUS_BAR_SWIPE_OVER_CHIP)
     fun handleInterceptTouchEventFromStatusBar_shadeReturnsFalse_flagOff_viewReturnsFalse() {
         `when`(shadeViewController.handleExternalInterceptTouch(any())).thenReturn(false)
@@ -432,6 +493,7 @@
                 fakeDarkIconDispatcher,
                 statusBarContentInsetsProviderStore,
                 Lazy { statusBarTouchShadeDisplayPolicy },
+                fakeShadeDisplaysRepository,
             )
             .create(view)
             .also { it.init() }
@@ -445,6 +507,7 @@
     }
 
     private companion object {
-        const val DISPLAY_ID = 1
+        const val DISPLAY_ID = 0
+        const val SECONDARY_DISPLAY_ID = 2
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
index 3e24fbe..b39e38b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
@@ -533,7 +533,7 @@
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
         when(mOngoingCallController.hasOngoingCall()).thenReturn(true);
-        when(mHeadsUpAppearanceController.shouldBeVisible()).thenReturn(true);
+        when(mHeadsUpAppearanceController.shouldHeadsUpStatusBarBeVisible()).thenReturn(true);
 
         fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
 
@@ -775,7 +775,7 @@
                 /* hasPrimaryOngoingActivity= */ true,
                 /* hasSecondaryOngoingActivity= */ false,
                 /* shouldAnimate= */ false);
-        when(mHeadsUpAppearanceController.shouldBeVisible()).thenReturn(true);
+        when(mHeadsUpAppearanceController.shouldHeadsUpStatusBarBeVisible()).thenReturn(true);
 
         fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
 
@@ -792,7 +792,7 @@
                 /* hasPrimaryOngoingActivity= */ true,
                 /* hasSecondaryOngoingActivity= */ true,
                 /* shouldAnimate= */ false);
-        when(mHeadsUpAppearanceController.shouldBeVisible()).thenReturn(true);
+        when(mHeadsUpAppearanceController.shouldHeadsUpStatusBarBeVisible()).thenReturn(true);
 
         fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
 
@@ -1091,9 +1091,9 @@
 
   @Test
   @DisableFlags({StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME})
-  public void disable_headsUpShouldBeVisibleTrue_clockDisabled() {
+  public void disable_shouldHeadsUpStatusBarBeVisibleTrue_clockDisabled() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
-        when(mHeadsUpAppearanceController.shouldBeVisible()).thenReturn(true);
+        when(mHeadsUpAppearanceController.shouldHeadsUpStatusBarBeVisible()).thenReturn(true);
 
         fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
 
@@ -1102,9 +1102,9 @@
 
   @Test
   @DisableFlags({StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME})
-  public void disable_headsUpShouldBeVisibleFalse_clockNotDisabled() {
+  public void disable_shouldHeadsUpStatusBarBeVisibleFalse_clockNotDisabled() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
-        when(mHeadsUpAppearanceController.shouldBeVisible()).thenReturn(false);
+        when(mHeadsUpAppearanceController.shouldHeadsUpStatusBarBeVisible()).thenReturn(false);
 
         fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
 
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/common/ui/data/repository/FakeConfigurationRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/common/ui/data/repository/FakeConfigurationRepository.kt
index 4d74254c..4870497 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/common/ui/data/repository/FakeConfigurationRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/common/ui/data/repository/FakeConfigurationRepository.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.common.ui.data.repository
 
 import android.content.res.Configuration
+import android.view.Display
 import com.android.systemui.dagger.SysUISingleton
 import dagger.Binds
 import dagger.Module
@@ -25,6 +26,7 @@
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableSharedFlow
 import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.asSharedFlow
 import kotlinx.coroutines.flow.asStateFlow
 
@@ -46,6 +48,10 @@
     override val configurationValues: Flow<Configuration> =
         _configurationChangeValues.asSharedFlow()
 
+    private val _onMovedToDisplay = MutableStateFlow<Int>(Display.DEFAULT_DISPLAY)
+    override val onMovedToDisplay: StateFlow<Int>
+        get() = _onMovedToDisplay
+
     private val _scaleForResolution = MutableStateFlow(1f)
     override val scaleForResolution: Flow<Float> = _scaleForResolution.asStateFlow()
 
@@ -64,6 +70,10 @@
         onAnyConfigurationChange()
     }
 
+    fun onMovedToDisplay(newDisplayId: Int) {
+        _onMovedToDisplay.value = newDisplayId
+    }
+
     fun setScaleForResolution(scale: Float) {
         _scaleForResolution.value = scale
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/EnableSceneContainer.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/EnableSceneContainer.kt
index 41402ba..4513cc0 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/EnableSceneContainer.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/EnableSceneContainer.kt
@@ -17,7 +17,6 @@
 package com.android.systemui.flags
 
 import android.platform.test.annotations.EnableFlags
-import com.android.systemui.Flags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR
 import com.android.systemui.Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR
 import com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT
 import com.android.systemui.Flags.FLAG_NOTIFICATION_AVALANCHE_THROTTLE_HUN
@@ -29,7 +28,6 @@
  * that feature. It is also picked up by [SceneContainerRule] to set non-aconfig prerequisites.
  */
 @EnableFlags(
-    FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR,
     FLAG_KEYGUARD_WM_STATE_REFACTOR,
     FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT,
     FLAG_NOTIFICATION_AVALANCHE_THROTTLE_HUN,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt
index 4cb8a41..2641070 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt
@@ -22,12 +22,14 @@
 import android.hardware.input.fakeInputManager
 import android.view.windowManager
 import com.android.systemui.broadcast.broadcastDispatcher
+import com.android.systemui.keyboard.shortcut.data.repository.AppLaunchDataRepository
 import com.android.systemui.keyboard.shortcut.data.repository.CustomInputGesturesRepository
 import com.android.systemui.keyboard.shortcut.data.repository.CustomShortcutCategoriesRepository
 import com.android.systemui.keyboard.shortcut.data.repository.DefaultShortcutCategoriesRepository
 import com.android.systemui.keyboard.shortcut.data.repository.InputGestureDataAdapter
 import com.android.systemui.keyboard.shortcut.data.repository.InputGestureMaps
 import com.android.systemui.keyboard.shortcut.data.repository.ShortcutCategoriesUtils
+import com.android.systemui.keyboard.shortcut.data.repository.ShortcutHelperInputDeviceRepository
 import com.android.systemui.keyboard.shortcut.data.repository.ShortcutHelperStateRepository
 import com.android.systemui.keyboard.shortcut.data.repository.ShortcutHelperTestHelper
 import com.android.systemui.keyboard.shortcut.data.source.AppCategoriesShortcutsSource
@@ -47,6 +49,7 @@
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.applicationCoroutineScope
 import com.android.systemui.kosmos.backgroundCoroutineContext
+import com.android.systemui.kosmos.backgroundScope
 import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.model.sysUiState
@@ -99,35 +102,54 @@
     Kosmos.Fixture {
         DefaultShortcutCategoriesRepository(
             applicationCoroutineScope,
-            testDispatcher,
             shortcutHelperSystemShortcutsSource,
             shortcutHelperMultiTaskingShortcutsSource,
             shortcutHelperAppCategoriesShortcutsSource,
             shortcutHelperInputShortcutsSource,
             shortcutHelperCurrentAppShortcutsSource,
-            fakeInputManager.inputManager,
-            shortcutHelperStateRepository,
+            shortcutHelperInputDeviceRepository,
             shortcutCategoriesUtils,
         )
     }
 
 val Kosmos.inputGestureMaps by Kosmos.Fixture { InputGestureMaps(applicationContext) }
 
-val Kosmos.inputGestureDataAdapter by Kosmos.Fixture { InputGestureDataAdapter(userTracker, inputGestureMaps, applicationContext)}
+val Kosmos.inputGestureDataAdapter by
+    Kosmos.Fixture { InputGestureDataAdapter(userTracker, inputGestureMaps, applicationContext) }
 
 val Kosmos.customInputGesturesRepository by
     Kosmos.Fixture { CustomInputGesturesRepository(userTracker, testDispatcher) }
 
+val Kosmos.shortcutHelperInputDeviceRepository by
+    Kosmos.Fixture {
+        ShortcutHelperInputDeviceRepository(
+            shortcutHelperStateRepository,
+            backgroundScope,
+            backgroundCoroutineContext,
+            fakeInputManager.inputManager,
+        )
+    }
+
+val Kosmos.appLaunchDataRepository by
+    Kosmos.Fixture {
+        AppLaunchDataRepository(
+            fakeInputManager.inputManager,
+            backgroundScope,
+            shortcutCategoriesUtils,
+            shortcutHelperInputDeviceRepository,
+        )
+    }
+
 val Kosmos.customShortcutCategoriesRepository by
     Kosmos.Fixture {
         CustomShortcutCategoriesRepository(
-            shortcutHelperStateRepository,
+            shortcutHelperInputDeviceRepository,
             applicationCoroutineScope,
-            testDispatcher,
             shortcutCategoriesUtils,
             inputGestureDataAdapter,
             customInputGesturesRepository,
             fakeInputManager.inputManager,
+            appLaunchDataRepository,
         )
     }
 
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
index 693ec79..1288d31 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
@@ -56,9 +56,6 @@
     override val animateBottomAreaDozingTransitions: StateFlow<Boolean> =
         _animateBottomAreaDozingTransitions
 
-    private val _bottomAreaAlpha = MutableStateFlow(1f)
-    override val bottomAreaAlpha: StateFlow<Float> = _bottomAreaAlpha
-
     private val _isKeyguardShowing = MutableStateFlow(false)
     override val isKeyguardShowing: StateFlow<Boolean> = _isKeyguardShowing
 
@@ -159,11 +156,6 @@
         _animateBottomAreaDozingTransitions.tryEmit(animate)
     }
 
-    @Deprecated("Deprecated as part of b/278057014")
-    override fun setBottomAreaAlpha(alpha: Float) {
-        _bottomAreaAlpha.value = alpha
-    }
-
     fun setKeyguardShowing(isShowing: Boolean) {
         _isKeyguardShowing.value = isShowing
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/DozeInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/DozeInteractorKosmos.kt
index 3304d44..10ca84f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/DozeInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/DozeInteractorKosmos.kt
@@ -19,11 +19,9 @@
 import com.android.systemui.keyguard.data.repository.keyguardRepository
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.power.data.repository.powerRepository
 import com.android.systemui.scene.domain.interactor.sceneInteractor
 
 val Kosmos.dozeInteractor: DozeInteractor by Fixture {
-    DozeInteractor(
-        keyguardRepository,
-        { sceneInteractor },
-    )
+    DozeInteractor(keyguardRepository, powerRepository, { sceneInteractor })
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractorKosmos.kt
index 4aa132c..8c9163d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractorKosmos.kt
@@ -43,5 +43,6 @@
             selectedUserInteractor,
             keyguardEnabledInteractor,
             keyguardServiceLockNowInteractor,
+            keyguardInteractor,
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToPrimaryBouncerTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToPrimaryBouncerTransitionViewModelKosmos.kt
index 2d1f836..b03624b 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToPrimaryBouncerTransitionViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToPrimaryBouncerTransitionViewModelKosmos.kt
@@ -26,5 +26,6 @@
 val Kosmos.alternateBouncerToPrimaryBouncerTransitionViewModel by Fixture {
     AlternateBouncerToPrimaryBouncerTransitionViewModel(
         animationFlow = keyguardTransitionAnimationFlow,
+        shadeDependentFlows = shadeDependentFlows,
     )
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardBottomAreaInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToPrimaryBouncerViewModelKosmos.kt
similarity index 60%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardBottomAreaInteractorKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToPrimaryBouncerViewModelKosmos.kt
index a3955f7..5e6d605 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardBottomAreaInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToPrimaryBouncerViewModelKosmos.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,14 +14,14 @@
  * limitations under the License.
  */
 
-package com.android.systemui.keyguard.domain.interactor
+package com.android.systemui.keyguard.ui.viewmodel
 
-import com.android.systemui.keyguard.data.repository.keyguardRepository
+import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
+import kotlinx.coroutines.ExperimentalCoroutinesApi
 
-val Kosmos.keyguardBottomAreaInteractor by Fixture {
-    KeyguardBottomAreaInteractor(
-        repository = keyguardRepository,
-    )
+@OptIn(ExperimentalCoroutinesApi::class)
+val Kosmos.aodToPrimaryBouncerTransitionViewModel by Fixture {
+    AodToPrimaryBouncerTransitionViewModel(animationFlow = keyguardTransitionAnimationFlow)
 }
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 3ab686d..e473107 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
@@ -51,6 +51,8 @@
             alternateBouncerToLockscreenTransitionViewModel,
         alternateBouncerToOccludedTransitionViewModel =
             alternateBouncerToOccludedTransitionViewModel,
+        alternateBouncerToPrimaryBouncerTransitionViewModel =
+            alternateBouncerToPrimaryBouncerTransitionViewModel,
         aodToGoneTransitionViewModel = aodToGoneTransitionViewModel,
         aodToLockscreenTransitionViewModel = aodToLockscreenTransitionViewModel,
         aodToOccludedTransitionViewModel = aodToOccludedTransitionViewModel,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardBottomAreaInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGlanceableHubTransitionViewModelKosmos.kt
similarity index 64%
rename from packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardBottomAreaInteractorKosmos.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGlanceableHubTransitionViewModelKosmos.kt
index a3955f7..09233af 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardBottomAreaInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGlanceableHubTransitionViewModelKosmos.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,14 +14,14 @@
  * limitations under the License.
  */
 
-package com.android.systemui.keyguard.domain.interactor
+package com.android.systemui.keyguard.ui.viewmodel
 
-import com.android.systemui.keyguard.data.repository.keyguardRepository
+import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
 
-val Kosmos.keyguardBottomAreaInteractor by Fixture {
-    KeyguardBottomAreaInteractor(
-        repository = keyguardRepository,
+val Kosmos.primaryBouncerToGlanceableHubTransitionViewModel by Fixture {
+    PrimaryBouncerToGlanceableHubTransitionViewModel(
+        animationFlow = keyguardTransitionAnimationFlow
     )
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModelKosmos.kt
index 370afc3..76478cb 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModelKosmos.kt
@@ -26,5 +26,6 @@
 val Kosmos.primaryBouncerToLockscreenTransitionViewModel by Fixture {
     PrimaryBouncerToLockscreenTransitionViewModel(
         animationFlow = keyguardTransitionAnimationFlow,
+        shadeDependentFlows = shadeDependentFlows,
     )
 }
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 7ee9d84..d941fb0 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
@@ -54,7 +54,7 @@
  * that kosmos instance
  */
 fun Kosmos.runTest(testBody: suspend Kosmos.() -> Unit) =
-    testScope.runTest { this@runTest.testBody() }
+    testScope.runTest testBody@{ this@runTest.testBody() }
 
 fun Kosmos.runCurrent() = testScope.runCurrent()
 
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/mediarouter/data/repository/FakeMediaRouterRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/mediarouter/data/repository/FakeMediaRouterRepository.kt
index d5637cb..8aa7a03 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/mediarouter/data/repository/FakeMediaRouterRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/mediarouter/data/repository/FakeMediaRouterRepository.kt
@@ -16,7 +16,6 @@
 
 package com.android.systemui.mediarouter.data.repository
 
-import android.media.projection.StopReason
 import com.android.systemui.statusbar.policy.CastDevice
 import kotlinx.coroutines.flow.MutableStateFlow
 
@@ -26,7 +25,7 @@
     var lastStoppedDevice: CastDevice? = null
         private set
 
-    override fun stopCasting(device: CastDevice, @StopReason stopReason: Int) {
+    override fun stopCasting(device: CastDevice) {
         lastStoppedDevice = device
     }
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/ActivityStarterKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/ActivityStarterKosmos.kt
index 0ec8d49..49bbbef 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/ActivityStarterKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/ActivityStarterKosmos.kt
@@ -17,6 +17,6 @@
 package com.android.systemui.plugins
 
 import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.util.mockito.mock
+import org.mockito.kotlin.mock
 
 var Kosmos.activityStarter by Kosmos.Fixture { mock<ActivityStarter>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/power/data/repository/FakePowerRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/power/data/repository/FakePowerRepository.kt
index ace6500..9dfbcfb 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/power/data/repository/FakePowerRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/power/data/repository/FakePowerRepository.kt
@@ -19,6 +19,7 @@
 
 import android.os.PowerManager
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.power.shared.model.DozeScreenStateModel
 import com.android.systemui.power.shared.model.ScreenPowerState
 import com.android.systemui.power.shared.model.WakeSleepReason
 import com.android.systemui.power.shared.model.WakefulnessModel
@@ -41,6 +42,8 @@
     private val _screenPowerState = MutableStateFlow(ScreenPowerState.SCREEN_OFF)
     override val screenPowerState = _screenPowerState.asStateFlow()
 
+    override val dozeScreenState = MutableStateFlow(DozeScreenStateModel.UNKNOWN)
+
     var lastWakeWhy: String? = null
     var lastWakeReason: Int? = null
 
@@ -63,7 +66,7 @@
         rawState: WakefulnessState,
         lastWakeReason: WakeSleepReason,
         lastSleepReason: WakeSleepReason,
-        powerButtonLaunchGestureTriggered: Boolean
+        powerButtonLaunchGestureTriggered: Boolean,
     ) {
         _wakefulness.value =
             WakefulnessModel(
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/QuickSettingsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/QuickSettingsKosmos.kt
index d72630d..01e357e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/QuickSettingsKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/QuickSettingsKosmos.kt
@@ -18,6 +18,7 @@
 
 import android.app.admin.devicePolicyManager
 import android.content.applicationContext
+import android.content.mockedContext
 import android.os.fakeExecutorHandler
 import android.os.looper
 import com.android.internal.logging.metricsLogger
@@ -54,9 +55,7 @@
 val Kosmos.fgsManagerController by Fixture { FakeFgsManagerController() }
 
 val Kosmos.footerActionsController by Fixture {
-    FooterActionsController(
-        fgsManagerController = fgsManagerController,
-    )
+    FooterActionsController(fgsManagerController = fgsManagerController)
 }
 
 val Kosmos.qsSecurityFooterUtils by Fixture {
@@ -86,6 +85,7 @@
         userSwitcherRepository = userSwitcherRepository,
         broadcastDispatcher = broadcastDispatcher,
         bgDispatcher = testDispatcher,
+        context = mockedContext,
     )
 }
 
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/footer/FooterActionsTestUtils.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/footer/FooterActionsTestUtils.kt
index cde5d4e..9edeb0c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/footer/FooterActionsTestUtils.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/footer/FooterActionsTestUtils.kt
@@ -69,6 +69,7 @@
     private val scheduler: TestCoroutineScheduler,
 ) {
     private val mockActivityStarter: ActivityStarter = mock<ActivityStarter>()
+
     /** Enable or disable the user switcher in the settings. */
     fun setUserSwitcherEnabled(settings: GlobalSettings, enabled: Boolean) {
         settings.putBool(Settings.Global.USER_SWITCHER_ENABLED, enabled)
@@ -110,6 +111,7 @@
         userSwitcherRepository: UserSwitcherRepository = userSwitcherRepository(),
         broadcastDispatcher: BroadcastDispatcher = mock(),
         bgDispatcher: CoroutineDispatcher = StandardTestDispatcher(scheduler),
+        context: Context = mock(),
     ): FooterActionsInteractor {
         return FooterActionsInteractorImpl(
             activityStarter,
@@ -124,6 +126,7 @@
             userSwitcherRepository,
             broadcastDispatcher,
             bgDispatcher,
+            context,
         )
     }
 
@@ -132,15 +135,12 @@
         securityController: SecurityController = FakeSecurityController(),
         bgDispatcher: CoroutineDispatcher = StandardTestDispatcher(scheduler),
     ): SecurityRepository {
-        return SecurityRepositoryImpl(
-            securityController,
-            bgDispatcher,
-        )
+        return SecurityRepositoryImpl(securityController, bgDispatcher)
     }
 
     /** Create a [SecurityRepository] to be used in tests. */
     fun foregroundServicesRepository(
-        fgsManagerController: FakeFgsManagerController = FakeFgsManagerController(),
+        fgsManagerController: FakeFgsManagerController = FakeFgsManagerController()
     ): ForegroundServicesRepository {
         return ForegroundServicesRepositoryImpl(fgsManagerController)
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/screenrecord/data/repository/FakeScreenRecordRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/screenrecord/data/repository/FakeScreenRecordRepository.kt
index 4c9e174..30b4763 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/screenrecord/data/repository/FakeScreenRecordRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/screenrecord/data/repository/FakeScreenRecordRepository.kt
@@ -16,7 +16,6 @@
 
 package com.android.systemui.screenrecord.data.repository
 
-import android.media.projection.StopReason
 import com.android.systemui.screenrecord.data.model.ScreenRecordModel
 import kotlinx.coroutines.flow.MutableStateFlow
 
@@ -26,7 +25,7 @@
 
     var stopRecordingInvoked = false
 
-    override suspend fun stopRecording(@StopReason stopReason: Int) {
+    override suspend fun stopRecording() {
         stopRecordingInvoked = true
     }
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/settings/BrightnessSliderControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/settings/BrightnessSliderControllerKosmos.kt
index 5d146fb..3d45a51 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/settings/BrightnessSliderControllerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/settings/BrightnessSliderControllerKosmos.kt
@@ -29,7 +29,7 @@
 /** This factory creates empty mocks. */
 var Kosmos.brightnessSliderControllerFactory by
     Kosmos.Fixture<BrightnessSliderController.Factory> {
-        BrightnessSliderController.Factory(
+        BrightnessSliderController.BrightnessSliderControllerFactory(
             falsingManager,
             uiEventLogger,
             vibratorHelper,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeTestUtil.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeTestUtil.kt
index 6944e6c..ab193d2 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeTestUtil.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeTestUtil.kt
@@ -261,7 +261,7 @@
     }
 
     private fun setIdleScene(scene: SceneKey) {
-        sceneInteractor.changeScene(scene, "test")
+        sceneInteractor.changeScene(scene, "ShadeTestUtil.setIdleScene")
         val transitionState =
             MutableStateFlow<ObservableTransitionState>(ObservableTransitionState.Idle(scene))
         sceneInteractor.setTransitionState(transitionState)
@@ -274,7 +274,7 @@
         progress: Float,
         isInitiatedByUserInput: Boolean = true,
     ) {
-        sceneInteractor.changeScene(from, "test")
+        sceneInteractor.changeScene(from, "ShadeTestUtil.setTransitionProgress")
         val transitionState =
             MutableStateFlow<ObservableTransitionState>(
                 ObservableTransitionState.Transition(
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepositoryKosmos.kt
index 7488397d..636cb37 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepositoryKosmos.kt
@@ -16,20 +16,53 @@
 
 package com.android.systemui.shade.data.repository
 
-import android.view.Display
+import com.android.systemui.display.data.repository.displayRepository
+import com.android.systemui.keyguard.data.repository.keyguardRepository
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
+import com.android.systemui.shade.display.AnyExternalShadeDisplayPolicy
+import com.android.systemui.shade.display.DefaultDisplayShadePolicy
 import com.android.systemui.shade.display.ShadeDisplayPolicy
-import com.android.systemui.shade.display.SpecificDisplayIdPolicy
+import com.android.systemui.shade.display.StatusBarTouchShadeDisplayPolicy
+import com.android.systemui.util.settings.fakeGlobalSettings
 
-val Kosmos.defaultShadeDisplayPolicy: ShadeDisplayPolicy by
-    Kosmos.Fixture { SpecificDisplayIdPolicy(Display.DEFAULT_DISPLAY) }
+val Kosmos.defaultShadeDisplayPolicy: DefaultDisplayShadePolicy by
+    Kosmos.Fixture { DefaultDisplayShadePolicy() }
+
+val Kosmos.anyExternalShadeDisplayPolicy: AnyExternalShadeDisplayPolicy by
+    Kosmos.Fixture {
+        AnyExternalShadeDisplayPolicy(
+            bgScope = testScope.backgroundScope,
+            displayRepository = displayRepository,
+        )
+    }
+
+val Kosmos.focusBasedShadeDisplayPolicy: StatusBarTouchShadeDisplayPolicy by
+    Kosmos.Fixture {
+        StatusBarTouchShadeDisplayPolicy(
+            displayRepository = displayRepository,
+            backgroundScope = testScope.backgroundScope,
+            keyguardRepository = keyguardRepository,
+            shadeOnDefaultDisplayWhenLocked = false,
+        )
+    }
 
 val Kosmos.shadeDisplaysRepository: MutableShadeDisplaysRepository by
     Kosmos.Fixture {
         ShadeDisplaysRepositoryImpl(
-            defaultPolicy = defaultShadeDisplayPolicy,
             bgScope = testScope.backgroundScope,
+            globalSettings = fakeGlobalSettings,
+            policies = shadeDisplayPolicies,
+            defaultPolicy = defaultShadeDisplayPolicy,
+        )
+    }
+
+val Kosmos.shadeDisplayPolicies: Set<ShadeDisplayPolicy> by
+    Kosmos.Fixture {
+        setOf(
+            defaultShadeDisplayPolicy,
+            anyExternalShadeDisplayPolicy,
+            focusBasedShadeDisplayPolicy,
         )
     }
 
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorKosmos.kt
index 00b788f..c0d2621 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorKosmos.kt
@@ -30,7 +30,6 @@
             backgroundScope = applicationCoroutineScope,
             shadeInteractor = shadeInteractorImpl,
             sceneInteractor = sceneInteractor,
-            lockIconViewController = mock(),
             shadeRepository = shadeRepository,
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/LockscreenShadeKeyguardTransitionControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/LockscreenShadeKeyguardTransitionControllerKosmos.kt
index e5a75d5..8865573 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/LockscreenShadeKeyguardTransitionControllerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/LockscreenShadeKeyguardTransitionControllerKosmos.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -18,8 +18,14 @@
 
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
-import com.android.systemui.util.mockito.mock
+import org.mockito.kotlin.mock
+
+var Kosmos.lockscreenShadeKeyguardTransitionController by Fixture {
+    mock<LockscreenShadeKeyguardTransitionController>()
+}
 
 var Kosmos.lockscreenShadeKeyguardTransitionControllerFactory by Fixture {
-    mock<LockscreenShadeKeyguardTransitionController.Factory>()
+    LockscreenShadeKeyguardTransitionController.Factory {
+        lockscreenShadeKeyguardTransitionController
+    }
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/LockscreenShadeQsTransitionControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/LockscreenShadeQsTransitionControllerKosmos.kt
index 2767980..fc52e45 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/LockscreenShadeQsTransitionControllerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/LockscreenShadeQsTransitionControllerKosmos.kt
@@ -18,8 +18,12 @@
 
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
-import com.android.systemui.util.mockito.mock
+import org.mockito.kotlin.mock
+
+var Kosmos.lockscreenShadeQsTransitionController by Fixture {
+    mock<LockscreenShadeQsTransitionController>()
+}
 
 var Kosmos.lockscreenShadeQsTransitionControllerFactory by Fixture {
-    mock<LockscreenShadeQsTransitionController.Factory>()
+    LockscreenShadeQsTransitionController.Factory { lockscreenShadeQsTransitionController }
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/SingleShadeLockScreenOverScrollerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/SingleShadeLockScreenOverScrollerKosmos.kt
index 43e39c0..5523bd6 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/SingleShadeLockScreenOverScrollerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/SingleShadeLockScreenOverScrollerKosmos.kt
@@ -18,8 +18,12 @@
 
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
-import com.android.systemui.util.mockito.mock
+import org.mockito.kotlin.mock
+
+var Kosmos.singleShadeLockScreenOverScroller by Fixture {
+    mock<SingleShadeLockScreenOverScroller>()
+}
 
 var Kosmos.singleShadeLockScreenOverScrollerFactory by Fixture {
-    mock<SingleShadeLockScreenOverScroller.Factory>()
+    SingleShadeLockScreenOverScroller.Factory { _ -> singleShadeLockScreenOverScroller }
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScrollerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScrollerKosmos.kt
index 017371a..e491dff 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScrollerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScrollerKosmos.kt
@@ -18,8 +18,10 @@
 
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
-import com.android.systemui.util.mockito.mock
+import org.mockito.kotlin.mock
+
+var Kosmos.splitShadeLockScreenOverScroller by Fixture { mock<SplitShadeLockScreenOverScroller>() }
 
 var Kosmos.splitShadeLockScreenOverScrollerFactory by Fixture {
-    mock<SplitShadeLockScreenOverScroller.Factory>()
+    SplitShadeLockScreenOverScroller.Factory { _, _ -> splitShadeLockScreenOverScroller }
 }
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 4bcce86..d0c80c7 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
@@ -19,8 +19,13 @@
 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.stack.domain.interactor.headsUpNotificationInteractor
 
 val Kosmos.notifChipsViewModel: NotifChipsViewModel by
     Kosmos.Fixture {
-        NotifChipsViewModel(applicationCoroutineScope, statusBarNotificationChipsInteractor)
+        NotifChipsViewModel(
+            applicationCoroutineScope,
+            statusBarNotificationChipsInteractor,
+            headsUpNotificationInteractor,
+        )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeStatusBarModeRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeStatusBarModeRepository.kt
index 22f8767..3c2d004 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeStatusBarModeRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeStatusBarModeRepository.kt
@@ -47,6 +47,7 @@
     override val isInFullscreenMode = MutableStateFlow(false)
     override val statusBarAppearance = MutableStateFlow<StatusBarAppearance?>(null)
     override val statusBarMode = MutableStateFlow(StatusBarMode.TRANSPARENT)
+    override val ongoingProcessRequiresStatusBarVisible = MutableStateFlow(false)
 
     override fun showTransient() {
         isTransientShown.value = true
@@ -59,6 +60,9 @@
     override fun start() {}
 
     override fun stop() {}
+    override fun setOngoingProcessRequiresStatusBarVisible(requiredVisible: Boolean) {
+        ongoingProcessRequiresStatusBarVisible.value = requiredVisible
+    }
 
     override fun onStatusBarViewInitialized(component: HomeStatusBarComponent) {}
 
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardBottomAreaInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/gesture/SwipeStatusBarAwayGestureHandlerKosmos.kt
similarity index 62%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardBottomAreaInteractorKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/gesture/SwipeStatusBarAwayGestureHandlerKosmos.kt
index a3955f7..72165c9 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardBottomAreaInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/gesture/SwipeStatusBarAwayGestureHandlerKosmos.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,14 +14,12 @@
  * limitations under the License.
  */
 
-package com.android.systemui.keyguard.domain.interactor
+package com.android.systemui.statusbar.gesture
 
-import com.android.systemui.keyguard.data.repository.keyguardRepository
 import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.kosmos.Kosmos.Fixture
+import org.mockito.kotlin.mock
 
-val Kosmos.keyguardBottomAreaInteractor by Fixture {
-    KeyguardBottomAreaInteractor(
-        repository = keyguardRepository,
-    )
-}
+val Kosmos.swipeStatusBarAwayGestureHandler: SwipeStatusBarAwayGestureHandler by
+Kosmos.Fixture {
+    mock<SwipeStatusBarAwayGestureHandler>()
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardBottomAreaInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/VisualInterruptionDecisionProviderKosmos.kt
similarity index 62%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardBottomAreaInteractorKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/VisualInterruptionDecisionProviderKosmos.kt
index a3955f7..360e9e9 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardBottomAreaInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/VisualInterruptionDecisionProviderKosmos.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,14 +14,11 @@
  * limitations under the License.
  */
 
-package com.android.systemui.keyguard.domain.interactor
+package com.android.systemui.statusbar.notification
 
-import com.android.systemui.keyguard.data.repository.keyguardRepository
 import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProvider
+import org.mockito.kotlin.mock
 
-val Kosmos.keyguardBottomAreaInteractor by Fixture {
-    KeyguardBottomAreaInteractor(
-        repository = keyguardRepository,
-    )
-}
+val Kosmos.visualInterruptionDecisionProvider by
+    Kosmos.Fixture { mock<VisualInterruptionDecisionProvider>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/repository/FakeHeadsUpRowRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/repository/FakeHeadsUpRowRepository.kt
index 7de22d8..4a692d2 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/repository/FakeHeadsUpRowRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/repository/FakeHeadsUpRowRepository.kt
@@ -26,7 +26,8 @@
         elementKey: Any = Any(),
         isPinned: Boolean,
     ) : this(key = key, elementKey = elementKey) {
-        this.pinnedStatus.value = if (isPinned) PinnedStatus.PinnedBySystem else PinnedStatus.NotPinned
+        this.pinnedStatus.value =
+            if (isPinned) PinnedStatus.PinnedBySystem else PinnedStatus.NotPinned
     }
 
     constructor(
@@ -40,3 +41,10 @@
     override val pinnedStatus: MutableStateFlow<PinnedStatus> =
         MutableStateFlow(PinnedStatus.NotPinned)
 }
+
+/** Use this fake if you're using [UnconfinedTestDispatcher]. See b/383528592 for reasoning. */
+class UnconfinedFakeHeadsUpRowRepository(
+    override val key: String,
+    override val elementKey: Any = Any(),
+    override val pinnedStatus: MutableStateFlow<PinnedStatus>,
+) : HeadsUpRowRepository
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardBottomAreaInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationAlertsInteractorKosmos.kt
similarity index 62%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardBottomAreaInteractorKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationAlertsInteractorKosmos.kt
index a3955f7..768952d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardBottomAreaInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationAlertsInteractorKosmos.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,14 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.systemui.keyguard.domain.interactor
+package com.android.systemui.statusbar.notification.domain.interactor
 
-import com.android.systemui.keyguard.data.repository.keyguardRepository
 import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.kosmos.Kosmos.Fixture
+import org.mockito.kotlin.mock
 
-val Kosmos.keyguardBottomAreaInteractor by Fixture {
-    KeyguardBottomAreaInteractor(
-        repository = keyguardRepository,
-    )
-}
+val Kosmos.notificationAlertsInteractor by Kosmos.Fixture { mock<NotificationAlertsInteractor>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractorKosmos.kt
index 51fb36f..40d9101 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractorKosmos.kt
@@ -20,7 +20,10 @@
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.applicationCoroutineScope
 import com.android.systemui.log.logcatLogBuffer
+import com.android.systemui.statusbar.data.repository.fakeStatusBarModeRepository
+import com.android.systemui.statusbar.gesture.swipeStatusBarAwayGestureHandler
 import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor
+import com.android.systemui.statusbar.window.fakeStatusBarWindowControllerStore
 
 val Kosmos.ongoingCallInteractor: OngoingCallInteractor by
     Kosmos.Fixture {
@@ -28,6 +31,9 @@
           scope = applicationCoroutineScope,
           activeNotificationsInteractor = activeNotificationsInteractor,
           activityManagerRepository = activityManagerRepository,
+          statusBarModeRepositoryStore = fakeStatusBarModeRepository,
+          statusBarWindowControllerStore = fakeStatusBarWindowControllerStore,
+          swipeStatusBarAwayGestureHandler = swipeStatusBarAwayGestureHandler,
           logBuffer = logcatLogBuffer("OngoingCallInteractorKosmos"),
       )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeCastController.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeCastController.kt
index da6b2ae..2df0c7a5 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeCastController.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeCastController.kt
@@ -16,7 +16,6 @@
 
 package com.android.systemui.statusbar.policy
 
-import android.media.projection.StopReason
 import java.io.PrintWriter
 
 class FakeCastController : CastController {
@@ -46,7 +45,7 @@
 
     override fun startCasting(device: CastDevice?) {}
 
-    override fun stopCasting(device: CastDevice?, @StopReason stopReason: Int) {
+    override fun stopCasting(device: CastDevice?) {
         lastStoppedDevice = device
     }
 
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeConfigurationController.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeConfigurationController.kt
index 3219127..13673d1 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeConfigurationController.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeConfigurationController.kt
@@ -27,6 +27,10 @@
         listeners.forEach { it.onConfigChanged(newConfiguration) }
     }
 
+    override fun dispatchOnMovedToDisplay(newDisplayId: Int, newConfiguration: Configuration) {
+        listeners.forEach { it.onMovedToDisplay(newDisplayId, newConfiguration) }
+    }
+
     override fun notifyThemeChanged() {
         listeners.forEach { it.onThemeChanged() }
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/KeyguardStateControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/KeyguardStateControllerKosmos.kt
index f19ac1e..26642d4 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/KeyguardStateControllerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/KeyguardStateControllerKosmos.kt
@@ -17,7 +17,7 @@
 package com.android.systemui.statusbar.policy
 
 import com.android.systemui.kosmos.Kosmos
-import org.mockito.Mockito.mock
+import org.mockito.kotlin.mock
 
 var Kosmos.keyguardStateController: KeyguardStateController by
-    Kosmos.Fixture { mock(KeyguardStateController::class.java) }
+    Kosmos.Fixture { mock<KeyguardStateController>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/FakeStatusBarWindowController.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/FakeStatusBarWindowController.kt
index a110a49..09e6a0e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/FakeStatusBarWindowController.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/FakeStatusBarWindowController.kt
@@ -20,6 +20,7 @@
 import android.view.ViewGroup
 import com.android.systemui.animation.ActivityTransitionAnimator
 import com.android.systemui.fragments.FragmentHostManager
+import kotlinx.coroutines.flow.MutableStateFlow
 import java.util.Optional
 
 class FakeStatusBarWindowController : StatusBarWindowController {
@@ -30,6 +31,8 @@
     var isStopped = false
         private set
 
+    val ongoingProcessRequiresStatusBarVisible = MutableStateFlow(false)
+
     override val statusBarHeight: Int = 0
 
     override fun refreshStatusBarHeight() {}
@@ -57,5 +60,7 @@
 
     override fun setForceStatusBarVisible(forceStatusBarVisible: Boolean) {}
 
-    override fun setOngoingProcessRequiresStatusBarVisible(visible: Boolean) {}
+    override fun setOngoingProcessRequiresStatusBarVisible(visible: Boolean) {
+        ongoingProcessRequiresStatusBarVisible.value = visible
+    }
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/touchpad/ui/gesture/FakeVelocityTracker.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/touchpad/ui/gesture/FakeVelocityTracker.kt
index f12089a..e767b87 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/touchpad/ui/gesture/FakeVelocityTracker.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/touchpad/ui/gesture/FakeVelocityTracker.kt
@@ -20,9 +20,9 @@
 import com.android.systemui.touchpad.tutorial.ui.gesture.Velocity
 import com.android.systemui.touchpad.tutorial.ui.gesture.VelocityTracker
 
-class FakeVelocityTracker : VelocityTracker {
+class FakeVelocityTracker(velocity: Float = 0f) : VelocityTracker {
 
-    private var fakeVelocity = Velocity(0f)
+    private var fakeVelocity = Velocity(velocity)
 
     override fun calculateVelocity(): Velocity {
         return fakeVelocity
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeConfigurationController.java b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeConfigurationController.java
index 111c40d..9cf25e8 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeConfigurationController.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeConfigurationController.java
@@ -16,6 +16,8 @@
 
 import android.content.res.Configuration;
 
+import androidx.annotation.NonNull;
+
 import com.android.systemui.statusbar.policy.ConfigurationController;
 
 public class FakeConfigurationController
@@ -43,4 +45,10 @@
     public String getNightModeName() {
         return "undefined";
     }
+
+    @Override
+    public void dispatchOnMovedToDisplay(int newDisplayId,
+            @NonNull Configuration newConfiguration) {
+
+    }
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/LeakCheckerCastController.java b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/LeakCheckerCastController.java
index 857dc85..2249bc0 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/LeakCheckerCastController.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/LeakCheckerCastController.java
@@ -16,7 +16,6 @@
 
 package com.android.systemui.utils.leaks;
 
-import android.media.projection.StopReason;
 import android.testing.LeakCheck;
 
 import com.android.systemui.statusbar.policy.CastController;
@@ -52,7 +51,7 @@
     }
 
     @Override
-    public void stopCasting(CastDevice device, @StopReason int stopReason) {
+    public void stopCasting(CastDevice device) {
 
     }
 
diff --git a/packages/Vcn/flags/Android.bp b/packages/Vcn/flags/Android.bp
index 3943c6f..8d09fdb 100644
--- a/packages/Vcn/flags/Android.bp
+++ b/packages/Vcn/flags/Android.bp
@@ -29,10 +29,24 @@
     ],
 }
 
+// TODO: b/374174952 Remove this library when VCN modularization is done
 java_aconfig_library {
     name: "android.net.vcn.flags-aconfig-java-export",
     aconfig_declarations: "android.net.vcn.flags-aconfig",
     mode: "exported",
     min_sdk_version: "35",
     defaults: ["framework-minus-apex-aconfig-java-defaults"],
+    apex_available: [
+        "//apex_available:platform",
+    ],
+}
+
+java_aconfig_library {
+    name: "android.net.vcn.flags-aconfig-java",
+    aconfig_declarations: "android.net.vcn.flags-aconfig",
+    min_sdk_version: "35",
+    defaults: ["framework-minus-apex-aconfig-java-defaults"],
+    apex_available: [
+        "com.android.tethering",
+    ],
 }
diff --git a/packages/Vcn/framework-b/Android.bp b/packages/Vcn/framework-b/Android.bp
index c312116..edb22c0 100644
--- a/packages/Vcn/framework-b/Android.bp
+++ b/packages/Vcn/framework-b/Android.bp
@@ -32,9 +32,9 @@
 }
 
 java_defaults {
-    name: "framework-connectivity-b-defaults",
+    name: "framework-connectivity-b-defaults-base",
     sdk_version: "module_current",
-    min_sdk_version: "35", // TODO: Make it Android 25Q2 when this is included in mainline
+
     defaults: ["framework-module-defaults"], // This is a boot jar
 
     srcs: [
@@ -44,14 +44,10 @@
 
     libs: [
         "android.net.ipsec.ike.stubs.module_lib",
-        "app-compat-annotations",
         "framework-wifi.stubs.module_lib",
         "unsupportedappusage",
     ],
-    static_libs: [
-        //TODO:375213246 Use a non-exported flag lib when VCN is in mainline
-        "android.net.vcn.flags-aconfig-java-export",
-    ],
+
     aidl: {
         include_dirs: [
             // For connectivity-framework classes such as Network.aidl, NetworkCapabilities.aidl
@@ -60,16 +56,83 @@
     },
 }
 
+soong_config_module_type {
+    name: "framework_connectivity_b_defaults_soong_config",
+    module_type: "java_defaults",
+    config_namespace: "ANDROID",
+    bool_variables: [
+        "is_vcn_in_mainline",
+    ],
+    properties: [
+        "min_sdk_version",
+        "static_libs",
+        "apex_available",
+    ],
+}
+
+framework_connectivity_b_defaults_soong_config {
+    name: "framework-connectivity-b-defaults",
+    defaults: [
+        "framework-connectivity-b-defaults-base",
+    ],
+    soong_config_variables: {
+        is_vcn_in_mainline: {
+            //TODO: b/380155299 Make it Baklava when aidl tool can understand it
+            min_sdk_version: "current",
+            static_libs: ["android.net.vcn.flags-aconfig-java"],
+            apex_available: ["com.android.tethering"],
+
+            conditions_default: {
+                min_sdk_version: "35",
+                static_libs: ["android.net.vcn.flags-aconfig-java-export"],
+                apex_available: ["//apex_available:platform"],
+            },
+        },
+    },
+}
+
+soong_config_module_type {
+    name: "framework_connectivity_b_java_sdk_library_defaults_soong_config",
+    module_type: "java_defaults",
+    config_namespace: "ANDROID",
+    bool_variables: [
+        "is_vcn_in_mainline",
+    ],
+    properties: [
+        "aconfig_declarations",
+        "jarjar_rules",
+    ],
+}
+
+framework_connectivity_b_java_sdk_library_defaults_soong_config {
+    name: "framework-connectivity-b-java-sdk-library-defaults",
+    soong_config_variables: {
+        is_vcn_in_mainline: {
+            aconfig_declarations: ["android.net.vcn.flags-aconfig-java"],
+
+            // TODO: b/375213246 Use the connectivity jarjar rule generator to create the
+            // jarjar rules. In the end state, use "framework-connectivity-jarjar-rules"
+            // after VCN code is moved to the Connectivity folder
+            jarjar_rules: "framework-vcn-jarjar-rules.txt",
+
+            conditions_default: {
+                aconfig_declarations: ["android.net.vcn.flags-aconfig-java-export"],
+
+                // Use "android.net.connectivity" as prefix would trigger
+                // "Hidden API flags are inconsistent" build error
+                jarjar_rules: "framework-vcn-jarjar-rules-platform.txt",
+            },
+        },
+    },
+}
+
 java_sdk_library {
     name: "framework-connectivity-b",
     defaults: [
         "framework-connectivity-b-defaults",
+        "framework-connectivity-b-java-sdk-library-defaults",
     ],
 
-    //TODO: b/375213246 Use "framework-connectivity-jarjar-rules" when VCN is
-    // in mainline
-    jarjar_rules: "framework-vcn-jarjar-rules.txt",
-
     permitted_packages: [
         "android.net",
         "android.net.vcn",
@@ -92,11 +155,6 @@
         "framework-connectivity-pre-jarjar",
     ],
 
-    aconfig_declarations: [
-        //TODO:375213246 Use a non-exported flag lib when VCN is in mainline
-        "android.net.vcn.flags-aconfig-java-export",
-    ],
-
     impl_library_visibility: [
         // Using for test only
         "//cts/tests/netlegacy22.api",
@@ -120,17 +178,13 @@
         "//packages/modules/Wifi/service/tests/wifitests",
     ],
 
-    apex_available: [
-        // TODO: b/374174952 Remove it when VCN modularization is released
-        "//apex_available:platform",
-
-        "com.android.tethering",
-    ],
+    visibility: ["//packages/modules/Connectivity:__subpackages__"],
 }
 
 java_library {
     name: "framework-connectivity-b-pre-jarjar",
     defaults: ["framework-connectivity-b-defaults"],
+    installable: false,
     libs: [
         "framework-connectivity-pre-jarjar",
     ],
diff --git a/packages/Vcn/framework-b/framework-vcn-jarjar-rules-platform.txt b/packages/Vcn/framework-b/framework-vcn-jarjar-rules-platform.txt
new file mode 100644
index 0000000..757043b
--- /dev/null
+++ b/packages/Vcn/framework-b/framework-vcn-jarjar-rules-platform.txt
@@ -0,0 +1,2 @@
+rule android.net.vcn.persistablebundleutils.** android.net.vcn.module.repackaged.persistablebundleutils.@1
+rule android.net.vcn.util.** android.net.vcn.module.repackaged.util.@1
\ No newline at end of file
diff --git a/packages/Vcn/framework-b/framework-vcn-jarjar-rules.txt b/packages/Vcn/framework-b/framework-vcn-jarjar-rules.txt
index 757043b..7e27b24 100644
--- a/packages/Vcn/framework-b/framework-vcn-jarjar-rules.txt
+++ b/packages/Vcn/framework-b/framework-vcn-jarjar-rules.txt
@@ -1,2 +1,2 @@
-rule android.net.vcn.persistablebundleutils.** android.net.vcn.module.repackaged.persistablebundleutils.@1
-rule android.net.vcn.util.** android.net.vcn.module.repackaged.util.@1
\ No newline at end of file
+rule android.net.vcn.persistablebundleutils.** android.net.connectivity.android.net.vcn.persistablebundleutils.@1
+rule android.net.vcn.util.** android.net.connectivity.android.net.vcn.util.@1
\ No newline at end of file
diff --git a/packages/Vcn/framework-b/src/android/net/ConnectivityFrameworkInitializerBaklava.java b/packages/Vcn/framework-b/src/android/net/ConnectivityFrameworkInitializerBaklava.java
index 1f0fa92..de22ca6 100644
--- a/packages/Vcn/framework-b/src/android/net/ConnectivityFrameworkInitializerBaklava.java
+++ b/packages/Vcn/framework-b/src/android/net/ConnectivityFrameworkInitializerBaklava.java
@@ -23,8 +23,6 @@
 import android.annotation.SystemApi;
 import android.app.SystemServiceRegistry;
 import android.compat.Compatibility;
-import android.compat.annotation.ChangeId;
-import android.compat.annotation.EnabledSince;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.net.vcn.IVcnManagementService;
@@ -40,17 +38,15 @@
 @FlaggedApi(FLAG_MAINLINE_VCN_MODULE_API)
 @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
 public final class ConnectivityFrameworkInitializerBaklava {
-    /**
-     * Starting with {@link VANILLA_ICE_CREAM}, Telephony feature flags (e.g. {@link
-     * PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}) are being checked before returning managers
-     * that depend on them. If the feature is missing, {@link Context#getSystemService} will return
-     * null.
-     *
-     * <p>This change is specific to VcnManager.
-     */
-    @ChangeId
-    @EnabledSince(targetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
-    private static final long ENABLE_CHECKING_TELEPHONY_FEATURES_FOR_VCN = 330902016;
+
+    // This is a copy of TelephonyFrameworkInitializer.ENABLE_CHECKING_TELEPHONY_FEATURES. This
+    // ChangeId will replace ENABLE_CHECKING_TELEPHONY_FEATURES_FOR_VCN to gate VcnManager
+    // feature flag enforcement.
+    // This replacement is safe because both ChangeIds have been enabled since Android V and serve
+    // the same purpose: enforcing telephony feature flag checks before using telephony-based
+    // features. This also simplifies VCN modularization by avoiding the need to handle different
+    // states, such as: SDK < B vs. SDK >= B; VCN in platform vs. VCN in the apex.
+    private static final long ENABLE_CHECKING_TELEPHONY_FEATURES = 330583731;
 
     /**
      * The corresponding vendor API for Android V
@@ -71,7 +67,7 @@
     private static String getVcnFeatureDependency() {
         // Check SDK version of the client app. Apps targeting pre-V SDK might
         // have not checked for existence of these features.
-        if (!Compatibility.isChangeEnabled(ENABLE_CHECKING_TELEPHONY_FEATURES_FOR_VCN)) {
+        if (!Compatibility.isChangeEnabled(ENABLE_CHECKING_TELEPHONY_FEATURES)) {
             return null;
         }
 
diff --git a/packages/Vcn/service-b/Android.bp b/packages/Vcn/service-b/Android.bp
index c1a1ee7..1370b06 100644
--- a/packages/Vcn/service-b/Android.bp
+++ b/packages/Vcn/service-b/Android.bp
@@ -32,11 +32,9 @@
     visibility: ["//frameworks/base/services/core"],
 }
 
-// Do not static include this lib in VCN because these files exist in
-// both service-connectivity.jar and framework.jar
-// TODO: b/374174952 After VCN moves to Connectivity/ and the modularization is done
-// this lib can be removed and "service-connectivity-b-pre-jarjar" can include
-// "service-connectivity-pre-jarjar"
+// TODO: b/374174952 This library is only used in "service-connectivity-b-platform"
+// After VCN moves to Connectivity/ and the modularization is done, this lib and
+// "service-connectivity-b-platform" can both be removed
 java_library {
     name: "connectivity-utils-service-vcn-internal",
     sdk_version: "module_current",
@@ -48,30 +46,30 @@
         "framework-annotations-lib",
         "unsupportedappusage",
     ],
-    visibility: [
-        "//visibility:private",
-    ],
-    apex_available: [
-        // TODO: b/374174952 Remove it when VCN modularization is released
-        "//apex_available:platform",
+    visibility: ["//visibility:private"],
+}
 
-        "com.android.tethering",
+filegroup {
+    name: "service-vcn-sources",
+    srcs: ["src/**/*.java"],
+    path: "src",
+    visibility: [
+        "//packages/modules/Connectivity/service-b",
     ],
 }
 
-java_library {
-    name: "service-connectivity-b-pre-jarjar",
-    sdk_version: "system_server_current",
-    min_sdk_version: "35", // TODO: Make it Android 25Q2 when this is included in mainline
+// This java_defaults will be used for "service-connectivity-b-platform" and
+// "service-connectivity-b-pre-jarjar"
+java_defaults {
+    name: "service-connectivity-b-pre-jarjar-defaults",
     defaults: ["framework-system-server-module-defaults"], // This is a system server jar
 
     srcs: [
-        "src/**/*.java",
+        ":service-vcn-sources",
     ],
 
     libs: [
         "android.net.ipsec.ike.stubs.module_lib",
-        "connectivity-utils-service-vcn-internal",
         "framework-annotations-lib",
         "framework-connectivity-pre-jarjar",
         "framework-connectivity-t-pre-jarjar",
@@ -89,13 +87,30 @@
         "modules-utils-handlerexecutor",
     ],
 
+    defaults_visibility: [
+        "//packages/modules/Connectivity/service-b",
+    ],
+}
+
+// This library is only used to be included into services.jar when the build system
+// flag RELEASE_MOVE_VCN_TO_MAINLINE is disabled. When the flag is enabled, a module
+// version of this library will be included in Tethering module
+java_library {
+    name: "service-connectivity-b-platform",
+    defaults: ["service-connectivity-b-pre-jarjar-defaults"],
+    static_libs: ["connectivity-utils-service-vcn-internal"],
+
+    sdk_version: "system_server_current",
+    min_sdk_version: "35",
+
+    // TODO (b/374174952 ): This file is for jarjaring files in
+    // "connectivity-utils-service-vcn-internal".
+    jarjar_rules: "service-vcn-platform-jarjar-rules.txt",
+
     visibility: [
         "//frameworks/base/services",
     ],
     apex_available: [
-        // TODO: b/374174952 Remove it when VCN modularization is released
         "//apex_available:platform",
-
-        "com.android.tethering",
     ],
 }
diff --git a/packages/Vcn/service-b/service-vcn-platform-jarjar-rules.txt b/packages/Vcn/service-b/service-vcn-platform-jarjar-rules.txt
new file mode 100644
index 0000000..3630727
--- /dev/null
+++ b/packages/Vcn/service-b/service-vcn-platform-jarjar-rules.txt
@@ -0,0 +1,5 @@
+rule android.util.IndentingPrintWriter android.net.vcn.module.repackaged.android.util.IndentingPrintWriter
+rule android.util.LocalLog android.net.vcn.module.repackaged.android.util.LocalLog
+rule com.android.internal.util.IndentingPrintWriter android.net.vcn.module.repackaged.com.android.internal.util.IndentingPrintWriter
+rule com.android.internal.util.MessageUtils android.net.vcn.module.repackaged.com.android.internal.util.MessageUtils
+rule com.android.internal.util.WakeupMessage android.net.vcn.module.repackaged.com.android.internal.util.WakeupMessage
\ No newline at end of file
diff --git a/packages/Vcn/service-b/src/com/android/server/ConnectivityServiceInitializerB.java b/packages/Vcn/service-b/src/com/android/server/ConnectivityServiceInitializerB.java
index 02c8ce4..81c7edf 100644
--- a/packages/Vcn/service-b/src/com/android/server/ConnectivityServiceInitializerB.java
+++ b/packages/Vcn/service-b/src/com/android/server/ConnectivityServiceInitializerB.java
@@ -16,7 +16,9 @@
 
 package com.android.server;
 
+import android.annotation.TargetApi;
 import android.content.Context;
+import android.os.Build;
 import android.util.Log;
 
 import com.android.tools.r8.keepanno.annotations.KeepItemKind;
@@ -30,6 +32,8 @@
 // Without this annotation, this class will be treated as unused class and be removed during build
 // time.
 @UsedByReflection(kind = KeepItemKind.CLASS_AND_METHODS)
+// TODO(b/374174952): Replace VANILLA_ICE_CREAM with BAKLAVA after Android B finalization
+@TargetApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
 public final class ConnectivityServiceInitializerB extends SystemService {
     private static final String TAG = ConnectivityServiceInitializerB.class.getSimpleName();
     private final VcnManagementService mVcnManagementService;
diff --git a/packages/Vcn/service-b/src/com/android/server/VcnManagementService.java b/packages/Vcn/service-b/src/com/android/server/VcnManagementService.java
index 26db6a9..c9a99d7 100644
--- a/packages/Vcn/service-b/src/com/android/server/VcnManagementService.java
+++ b/packages/Vcn/service-b/src/com/android/server/VcnManagementService.java
@@ -37,6 +37,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SuppressLint;
+import android.annotation.TargetApi;
 import android.app.AppOpsManager;
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -164,6 +165,8 @@
  * @hide
  */
 // TODO(b/180451994): ensure all incoming + outgoing calls have a cleared calling identity
+// TODO(b/374174952): Replace VANILLA_ICE_CREAM with BAKLAVA after Android B finalization
+@TargetApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
 public class VcnManagementService extends IVcnManagementService.Stub {
     @NonNull private static final String TAG = VcnManagementService.class.getSimpleName();
     @NonNull private static final String CONTEXT_ATTRIBUTION_TAG = "VCN";
@@ -297,8 +300,10 @@
         });
     }
 
-    // Package-visibility for SystemServer to create instances.
-    static VcnManagementService create(@NonNull Context context) {
+    /** Called by ConnectivityServiceInitializerB to create instances. */
+    // VcnManagementService will be jarjared but ConnectivityServiceInitializerB will not. Thus this
+    // method needs to be public for ConnectivityServiceInitializerB to access
+    public static VcnManagementService create(@NonNull Context context) {
         return new VcnManagementService(context, new Dependencies());
     }
 
diff --git a/packages/Vcn/service-b/src/com/android/server/vcn/TelephonySubscriptionTracker.java b/packages/Vcn/service-b/src/com/android/server/vcn/TelephonySubscriptionTracker.java
index b448f75..b04e25d 100644
--- a/packages/Vcn/service-b/src/com/android/server/vcn/TelephonySubscriptionTracker.java
+++ b/packages/Vcn/service-b/src/com/android/server/vcn/TelephonySubscriptionTracker.java
@@ -22,12 +22,14 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.TargetApi;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.net.vcn.VcnManager;
 import android.net.vcn.util.PersistableBundleUtils.PersistableBundleWrapper;
+import android.os.Build;
 import android.os.Handler;
 import android.os.ParcelUuid;
 import android.os.PersistableBundle;
@@ -77,6 +79,8 @@
  *
  * @hide
  */
+// TODO(b/374174952): Replace VANILLA_ICE_CREAM with BAKLAVA after Android B finalization
+@TargetApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
 public class TelephonySubscriptionTracker extends BroadcastReceiver {
     @NonNull private static final String TAG = TelephonySubscriptionTracker.class.getSimpleName();
     private static final boolean LOG_DBG = false; // STOPSHIP if true
diff --git a/packages/Vcn/service-b/src/com/android/server/vcn/Vcn.java b/packages/Vcn/service-b/src/com/android/server/vcn/Vcn.java
index 2524d0e..369ef6a 100644
--- a/packages/Vcn/service-b/src/com/android/server/vcn/Vcn.java
+++ b/packages/Vcn/service-b/src/com/android/server/vcn/Vcn.java
@@ -29,6 +29,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.TargetApi;
 import android.content.ContentResolver;
 import android.database.ContentObserver;
 import android.net.NetworkCapabilities;
@@ -39,6 +40,7 @@
 import android.net.vcn.VcnGatewayConnectionConfig;
 import android.net.vcn.VcnManager.VcnErrorCode;
 import android.net.vcn.util.LogUtils;
+import android.os.Build;
 import android.os.Handler;
 import android.os.Message;
 import android.os.ParcelUuid;
@@ -75,6 +77,8 @@
  *
  * @hide
  */
+// TODO(b/374174952): Replace VANILLA_ICE_CREAM with BAKLAVA after Android B finalization
+@TargetApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
 public class Vcn extends Handler {
     private static final String TAG = Vcn.class.getSimpleName();
 
diff --git a/packages/Vcn/service-b/src/com/android/server/vcn/VcnGatewayConnection.java b/packages/Vcn/service-b/src/com/android/server/vcn/VcnGatewayConnection.java
index e50fc3a..300b80f 100644
--- a/packages/Vcn/service-b/src/com/android/server/vcn/VcnGatewayConnection.java
+++ b/packages/Vcn/service-b/src/com/android/server/vcn/VcnGatewayConnection.java
@@ -37,6 +37,8 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.annotation.TargetApi;
 import android.content.Context;
 import android.net.ConnectivityDiagnosticsManager;
 import android.net.ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback;
@@ -82,6 +84,7 @@
 import android.net.vcn.util.MtuUtils;
 import android.net.vcn.util.OneWayBoolean;
 import android.net.wifi.WifiInfo;
+import android.os.Build;
 import android.os.Handler;
 import android.os.Message;
 import android.os.ParcelUuid;
@@ -171,6 +174,8 @@
  *
  * @hide
  */
+// TODO(b/374174952): Replace VANILLA_ICE_CREAM with BAKLAVA after Android B finalization
+@TargetApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
 public class VcnGatewayConnection extends StateMachine {
     private static final String TAG = VcnGatewayConnection.class.getSimpleName();
 
@@ -2942,6 +2947,10 @@
          *
          * <p>Synchronize this action to minimize locking around WakeLock use.
          */
+        // WakelockTimeout suppressed because the time the wake lock is needed for is unknown. The
+        // wakelock is only acquired when a Message is sent to this state machine and will be
+        // released when the message is processed or the state machin quits
+        @SuppressLint("WakelockTimeout")
         public synchronized void acquire() {
             mImpl.acquire();
         }
diff --git a/packages/Vcn/service-b/src/com/android/server/vcn/VcnNetworkProvider.java b/packages/Vcn/service-b/src/com/android/server/vcn/VcnNetworkProvider.java
index 4552f50..99c848f 100644
--- a/packages/Vcn/service-b/src/com/android/server/vcn/VcnNetworkProvider.java
+++ b/packages/Vcn/service-b/src/com/android/server/vcn/VcnNetworkProvider.java
@@ -25,6 +25,7 @@
 import static com.android.server.VcnManagementService.VDBG;
 
 import android.annotation.NonNull;
+import android.annotation.TargetApi;
 import android.content.Context;
 import android.net.ConnectivityManager;
 import android.net.NetworkCapabilities;
@@ -32,6 +33,7 @@
 import android.net.NetworkRequest;
 import android.net.NetworkScore;
 import android.net.vcn.VcnGatewayConnectionConfig;
+import android.os.Build;
 import android.os.Handler;
 import android.os.Looper;
 import android.util.ArraySet;
@@ -54,6 +56,8 @@
  *
  * @hide
  */
+// TODO(b/374174952): Replace VANILLA_ICE_CREAM with BAKLAVA after Android B finalization
+@TargetApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
 public class VcnNetworkProvider extends NetworkProvider {
     private static final String TAG = VcnNetworkProvider.class.getSimpleName();
 
diff --git a/packages/Vcn/service-b/src/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java b/packages/Vcn/service-b/src/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java
index 72de613..6467af4 100644
--- a/packages/Vcn/service-b/src/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java
+++ b/packages/Vcn/service-b/src/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java
@@ -23,6 +23,7 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.TargetApi;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -31,6 +32,7 @@
 import android.net.IpSecTransformState;
 import android.net.Network;
 import android.net.vcn.VcnManager;
+import android.os.Build;
 import android.os.Handler;
 import android.os.OutcomeReceiver;
 import android.os.PowerManager;
@@ -59,6 +61,8 @@
  *
  * <p>This class is flag gated by "network_metric_monitor" and "ipsec_tramsform_state"
  */
+// TODO(b/374174952) Replace VANILLA_ICE_CREAM with BAKLAVA after Android B finalization
+@TargetApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
 public class IpSecPacketLossDetector extends NetworkMetricMonitor {
     private static final String TAG = IpSecPacketLossDetector.class.getSimpleName();
 
diff --git a/packages/Vcn/service-b/src/com/android/server/vcn/routeselection/NetworkMetricMonitor.java b/packages/Vcn/service-b/src/com/android/server/vcn/routeselection/NetworkMetricMonitor.java
index 86cee55..1485344 100644
--- a/packages/Vcn/service-b/src/com/android/server/vcn/routeselection/NetworkMetricMonitor.java
+++ b/packages/Vcn/service-b/src/com/android/server/vcn/routeselection/NetworkMetricMonitor.java
@@ -22,9 +22,11 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.TargetApi;
 import android.net.IpSecTransform;
 import android.net.IpSecTransformState;
 import android.net.Network;
+import android.os.Build;
 import android.os.OutcomeReceiver;
 import android.util.CloseGuard;
 import android.util.Slog;
@@ -42,6 +44,8 @@
  *
  * <p>This class is flag gated by "network_metric_monitor"
  */
+// TODO(b/374174952): Replace VANILLA_ICE_CREAM with BAKLAVA after Android B finalization
+@TargetApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
 public abstract class NetworkMetricMonitor implements AutoCloseable {
     private static final String TAG = NetworkMetricMonitor.class.getSimpleName();
 
diff --git a/packages/Vcn/service-b/src/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java b/packages/Vcn/service-b/src/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java
index 79c4116..705141f 100644
--- a/packages/Vcn/service-b/src/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java
+++ b/packages/Vcn/service-b/src/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java
@@ -29,12 +29,14 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.TargetApi;
 import android.net.NetworkCapabilities;
 import android.net.TelephonyNetworkSpecifier;
 import android.net.vcn.VcnCellUnderlyingNetworkTemplate;
 import android.net.vcn.VcnManager;
 import android.net.vcn.VcnUnderlyingNetworkTemplate;
 import android.net.vcn.VcnWifiUnderlyingNetworkTemplate;
+import android.os.Build;
 import android.os.ParcelUuid;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
@@ -50,6 +52,8 @@
 import java.util.Set;
 
 /** @hide */
+// TODO(b/374174952): Replace VANILLA_ICE_CREAM with BAKLAVA after Android B finalization
+@TargetApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
 class NetworkPriorityClassifier {
     @NonNull private static final String TAG = NetworkPriorityClassifier.class.getSimpleName();
     /**
diff --git a/packages/Vcn/service-b/src/com/android/server/vcn/routeselection/UnderlyingNetworkController.java b/packages/Vcn/service-b/src/com/android/server/vcn/routeselection/UnderlyingNetworkController.java
index 29a0762..bc552e7 100644
--- a/packages/Vcn/service-b/src/com/android/server/vcn/routeselection/UnderlyingNetworkController.java
+++ b/packages/Vcn/service-b/src/com/android/server/vcn/routeselection/UnderlyingNetworkController.java
@@ -28,6 +28,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.TargetApi;
 import android.net.ConnectivityManager;
 import android.net.ConnectivityManager.NetworkCallback;
 import android.net.IpSecTransform;
@@ -40,6 +41,7 @@
 import android.net.vcn.VcnGatewayConnectionConfig;
 import android.net.vcn.VcnUnderlyingNetworkTemplate;
 import android.net.vcn.util.LogUtils;
+import android.os.Build;
 import android.os.Handler;
 import android.os.ParcelUuid;
 import android.telephony.TelephonyCallback;
@@ -73,6 +75,8 @@
  *
  * @hide
  */
+// TODO(b/374174952): Replace VANILLA_ICE_CREAM with BAKLAVA after Android B finalization
+@TargetApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
 public class UnderlyingNetworkController {
     @NonNull private static final String TAG = UnderlyingNetworkController.class.getSimpleName();
 
diff --git a/packages/Vcn/service-b/src/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluator.java b/packages/Vcn/service-b/src/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluator.java
index 30f4ed1..776931b 100644
--- a/packages/Vcn/service-b/src/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluator.java
+++ b/packages/Vcn/service-b/src/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluator.java
@@ -22,12 +22,14 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.TargetApi;
 import android.net.IpSecTransform;
 import android.net.LinkProperties;
 import android.net.Network;
 import android.net.NetworkCapabilities;
 import android.net.vcn.VcnManager;
 import android.net.vcn.VcnUnderlyingNetworkTemplate;
+import android.os.Build;
 import android.os.Handler;
 import android.os.ParcelUuid;
 import android.util.IndentingPrintWriter;
@@ -50,6 +52,8 @@
  *
  * @hide
  */
+// TODO(b/374174952): Replace VANILLA_ICE_CREAM with BAKLAVA after Android B finalization
+@TargetApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
 public class UnderlyingNetworkEvaluator {
     private static final String TAG = UnderlyingNetworkEvaluator.class.getSimpleName();
 
diff --git a/ravenwood/scripts/extract-last-soong-commands.py b/ravenwood/scripts/extract-last-soong-commands.py
index bdc1de0..c08d4aa 100755
--- a/ravenwood/scripts/extract-last-soong-commands.py
+++ b/ravenwood/scripts/extract-last-soong-commands.py
@@ -48,6 +48,7 @@
     with open(outfile, "w") as out:
         out.write(HEADER)
 
+        count = 0
         with gzip.open(log) as f:
             for line in f:
                 s = line.decode("utf-8")
@@ -63,7 +64,8 @@
                 if m:
                     command = m.groups()[0]
 
-                    out.write('#========\n')
+                    count += 1
+                    out.write(f'### Command {count} ========\n')
 
                     # Show the full command line before executing it.
                     out.write('#echo ' + shlex.quote(command) + '\n')
diff --git a/services/Android.bp b/services/Android.bp
index a7cb9bb..efd35ce 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -233,8 +233,7 @@
             libs: ["service-ondeviceintelligence.stubs.system_server"],
         },
         release_ondevice_intelligence_platform: {
-            srcs: [":service-ondeviceintelligence-sources"],
-            static_libs: ["modules-utils-backgroundthread"],
+            srcs: [":service-ondeviceintelligence-sources-platform"],
         },
     },
 }
@@ -245,13 +244,21 @@
     name: "system_java_library",
     module_type: "java_library",
     config_namespace: "system_services",
-    bool_variables: ["without_vibrator"],
+    variables: ["without_hal"],
     properties: ["vintf_fragment_modules"],
 }
 
+soong_config_string_variable {
+    name: "without_hal",
+    values: [
+        "vibrator",
+        "devicestate",
+    ],
+}
+
 vintf_fragment {
-    name: "manifest_services.xml",
-    src: "manifest_services.xml",
+    name: "manifest_services_android.frameworks.location.xml",
+    src: "manifest_services_android.frameworks.location.xml",
 }
 
 vintf_fragment {
@@ -259,6 +266,11 @@
     src: "manifest_services_android.frameworks.vibrator.xml",
 }
 
+vintf_fragment {
+    name: "manifest_services_android.frameworks.devicestate.xml",
+    src: "manifest_services_android.frameworks.devicestate.xml",
+}
+
 system_java_library {
     name: "services",
     defaults: [
@@ -312,9 +324,11 @@
         "services.wifi",
         "service-blobstore",
         "service-jobscheduler",
-        "service-connectivity-b-pre-jarjar", // Move it to mainline module
         "android.hidl.base-V1.0-java",
-    ],
+    ] + select(release_flag("RELEASE_MOVE_VCN_TO_MAINLINE"), {
+        true: [],
+        default: ["service-connectivity-b-platform"],
+    }),
 
     libs: [
         "android.hidl.manager-V1.0-java",
@@ -327,14 +341,24 @@
     ],
 
     soong_config_variables: {
-        without_vibrator: {
-            vintf_fragment_modules: [
-                "manifest_services.xml",
-            ],
+        without_hal: {
+            vibrator: {
+                vintf_fragment_modules: [
+                    "manifest_services_android.frameworks.location.xml",
+                    "manifest_services_android.frameworks.devicestate.xml",
+                ],
+            },
+            devicestate: {
+                vintf_fragment_modules: [
+                    "manifest_services_android.frameworks.location.xml",
+                    "manifest_services_android.frameworks.vibrator.xml",
+                ],
+            },
             conditions_default: {
                 vintf_fragment_modules: [
-                    "manifest_services.xml",
+                    "manifest_services_android.frameworks.location.xml",
                     "manifest_services_android.frameworks.vibrator.xml",
+                    "manifest_services_android.frameworks.devicestate.xml",
                 ],
             },
         },
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
index e1b6c9c..5133575 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
@@ -245,22 +245,45 @@
                     final boolean complete =
                             event.getAction() == KeyGestureEvent.ACTION_GESTURE_COMPLETE
                                     && !event.isCancelled();
+
+                    // TODO(b/355499907): Receive and handle held key gestures, which can be used
+                    // for continuous scaling and panning. In addition, handle multiple pan gestures
+                    // at the same time (e.g. user may try to pan diagonally) reasonably, including
+                    // decreasing diagonal movement by sqrt(2) to make it appear the same speed
+                    // as non-diagonal movement.
+
+                    if (!complete) {
+                        return false;
+                    }
+
                     final int gestureType = event.getKeyGestureType();
                     final int displayId = isDisplayIdValid(event.getDisplayId())
                             ? event.getDisplayId() : Display.DEFAULT_DISPLAY;
 
                     switch (gestureType) {
                         case KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFIER_ZOOM_IN:
-                            if (complete) {
                                 mAms.getMagnificationController().scaleMagnificationByStep(
                                         displayId, MagnificationController.ZOOM_DIRECTION_IN);
-                            }
                             return true;
                         case KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFIER_ZOOM_OUT:
-                            if (complete) {
                                 mAms.getMagnificationController().scaleMagnificationByStep(
                                         displayId, MagnificationController.ZOOM_DIRECTION_OUT);
-                            }
+                            return true;
+                        case KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFIER_PAN_LEFT:
+                            mAms.getMagnificationController().panMagnificationByStep(
+                                    displayId, MagnificationController.PAN_DIRECTION_LEFT);
+                            return true;
+                        case KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFIER_PAN_RIGHT:
+                            mAms.getMagnificationController().panMagnificationByStep(
+                                    displayId, MagnificationController.PAN_DIRECTION_RIGHT);
+                            return true;
+                        case KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFIER_PAN_UP:
+                            mAms.getMagnificationController().panMagnificationByStep(
+                                    displayId, MagnificationController.PAN_DIRECTION_UP);
+                            return true;
+                        case KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFIER_PAN_DOWN:
+                            mAms.getMagnificationController().panMagnificationByStep(
+                                    displayId, MagnificationController.PAN_DIRECTION_DOWN);
                             return true;
                     }
                     return false;
@@ -270,7 +293,11 @@
                 public boolean isKeyGestureSupported(int gestureType) {
                     return switch (gestureType) {
                         case KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFIER_ZOOM_IN,
-                             KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFIER_ZOOM_OUT -> true;
+                             KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFIER_ZOOM_OUT,
+                             KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFIER_PAN_LEFT,
+                             KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFIER_PAN_RIGHT,
+                             KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFIER_PAN_UP,
+                             KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFIER_PAN_DOWN -> true;
                         default -> false;
                     };
                 }
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 5c1ad74..37d045b 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -1057,17 +1057,20 @@
                             intent.getStringExtra(Intent.EXTRA_SETTING_NEW_VALUE);
                     final int restoredFromSdk =
                             intent.getIntExtra(Intent.EXTRA_SETTING_RESTORED_FROM_SDK_INT, 0);
+                    final int userId =
+                            android.view.accessibility.Flags.restoreA11ySecureSettingsOnHsumDevice()
+                                    ? getSendingUserId() : UserHandle.USER_SYSTEM;
                     switch (which) {
                         case Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES -> {
                             synchronized (mLock) {
                                 restoreEnabledAccessibilityServicesLocked(
-                                        previousValue, newValue, restoredFromSdk);
+                                        previousValue, newValue, restoredFromSdk, userId);
                             }
                         }
                         case ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED -> {
                             synchronized (mLock) {
                                 restoreLegacyDisplayMagnificationNavBarIfNeededLocked(
-                                        newValue, restoredFromSdk);
+                                        newValue, restoredFromSdk, userId);
                             }
                         }
                         // Currently in SUW, the user can't see gesture shortcut option as the
@@ -1078,7 +1081,7 @@
                              Settings.Secure.ACCESSIBILITY_QS_TARGETS,
                              Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE ->
                                 restoreShortcutTargets(newValue,
-                                        ShortcutUtils.convertToType(which));
+                                        ShortcutUtils.convertToType(which), userId);
                     }
                 }
             }
@@ -1144,10 +1147,10 @@
         }
     }
 
-    // Called only during settings restore; currently supports only the owner user
-    // TODO: b/22388012
-    private void restoreLegacyDisplayMagnificationNavBarIfNeededLocked(String newSetting,
-            int restoreFromSdkInt) {
+    // Called only during settings restore; currently supports only the main user
+    // TODO: http://b/374830726
+    private void restoreLegacyDisplayMagnificationNavBarIfNeededLocked(
+            String newSetting, int restoreFromSdkInt, int userId) {
         if (restoreFromSdkInt >= Build.VERSION_CODES.R) {
             return;
         }
@@ -1160,7 +1163,7 @@
             return;
         }
 
-        final AccessibilityUserState userState = getUserStateLocked(UserHandle.USER_SYSTEM);
+        final AccessibilityUserState userState = getUserStateLocked(userId);
         final Set<String> targetsFromSetting = new ArraySet<>();
         readColonDelimitedSettingToSet(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS,
                 userState.mUserId, str -> str, targetsFromSetting);
@@ -2225,20 +2228,20 @@
         getMagnificationController().onUserRemoved(userId);
     }
 
-    // Called only during settings restore; currently supports only the owner user
-    // TODO: http://b/22388012
-    void restoreEnabledAccessibilityServicesLocked(String oldSetting, String newSetting,
-            int restoreFromSdkInt) {
+    // Called only during settings restore; currently supports only the main user
+    // TODO: http://b/374830726
+    void restoreEnabledAccessibilityServicesLocked(
+            String oldSetting, String newSetting, int restoreFromSdkInt, int userId) {
         readComponentNamesFromStringLocked(oldSetting, mTempComponentNameSet, false);
         readComponentNamesFromStringLocked(newSetting, mTempComponentNameSet, true);
 
-        AccessibilityUserState userState = getUserStateLocked(UserHandle.USER_SYSTEM);
+        AccessibilityUserState userState = getUserStateLocked(userId);
         userState.mEnabledServices.clear();
         userState.mEnabledServices.addAll(mTempComponentNameSet);
         persistComponentNamesToSettingLocked(
                 Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
                 userState.mEnabledServices,
-                UserHandle.USER_SYSTEM);
+                userState.mUserId);
         onUserStateChangedLocked(userState);
         migrateAccessibilityButtonSettingsIfNecessaryLocked(userState, null, restoreFromSdkInt);
     }
@@ -2247,21 +2250,22 @@
      * User could configure accessibility shortcut during the SUW before restoring user data.
      * Merges the current value and the new value to make sure we don't lost the setting the user's
      * preferences of accessibility shortcut updated in SUW are not lost.
-     * Called only during settings restore; currently supports only the owner user.
+     *
      * <P>
      * Throws an exception if used with {@code TRIPLETAP} or {@code TWOFINGER_DOUBLETAP}.
      * </P>
-     * TODO: http://b/22388012
      */
-    private void restoreShortcutTargets(String newValue,
-            @UserShortcutType int shortcutType) {
+    // Called only during settings restore; currently supports only the main user.
+    // TODO: http://b/374830726
+    private void restoreShortcutTargets(
+            String newValue, @UserShortcutType int shortcutType, int userId) {
         assertNoTapShortcut(shortcutType);
         if (shortcutType == QUICK_SETTINGS && !android.view.accessibility.Flags.a11yQsShortcut()) {
             return;
         }
 
         synchronized (mLock) {
-            final AccessibilityUserState userState = getUserStateLocked(UserHandle.USER_SYSTEM);
+            final AccessibilityUserState userState = getUserStateLocked(userId);
             final Set<String> mergedTargets = (shortcutType == HARDWARE)
                     ? new ArraySet<>(ShortcutUtils.getShortcutTargetsFromSettings(
                             mContext, shortcutType, userState.mUserId))
@@ -2295,7 +2299,7 @@
 
             userState.updateShortcutTargetsLocked(mergedTargets, shortcutType);
             persistColonDelimitedSetToSettingLocked(ShortcutUtils.convertToKey(shortcutType),
-                    UserHandle.USER_SYSTEM, mergedTargets, str -> str);
+                    userState.mUserId, mergedTargets, str -> str);
             scheduleNotifyClientsOfServicesStateChangeLocked(userState);
             onUserStateChangedLocked(userState);
         }
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
index 058b2be..2e131b6 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
@@ -35,14 +35,19 @@
 import android.graphics.PointF;
 import android.graphics.Rect;
 import android.graphics.Region;
+import android.hardware.display.DisplayManager;
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.provider.Settings;
+import android.util.DisplayMetrics;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
+import android.util.SparseDoubleArray;
 import android.util.SparseIntArray;
 import android.util.SparseLongArray;
+import android.util.TypedValue;
+import android.view.Display;
 import android.view.accessibility.MagnificationAnimationCallback;
 
 import com.android.internal.accessibility.util.AccessibilityStatsLogUtils;
@@ -102,6 +107,7 @@
     /** Whether the platform supports window magnification feature. */
     private final boolean mSupportWindowMagnification;
     private final MagnificationScaleStepProvider mScaleStepProvider;
+    private final MagnificationPanStepProvider mPanStepProvider;
 
     private final Executor mBackgroundExecutor;
 
@@ -132,7 +138,7 @@
             .UiChangesForAccessibilityCallbacks> mAccessibilityCallbacksDelegateArray =
             new SparseArray<>();
 
-    // Direction magnifier scale can be altered.
+    // Direction magnification scale can be altered.
     public static final int ZOOM_DIRECTION_IN = 0;
     public static final int ZOOM_DIRECTION_OUT = 1;
 
@@ -140,6 +146,16 @@
     public @interface ZoomDirection {
     }
 
+    // Directions magnification center can be moved.
+    public static final int PAN_DIRECTION_LEFT = 0;
+    public static final int PAN_DIRECTION_RIGHT = 1;
+    public static final int PAN_DIRECTION_UP = 2;
+    public static final int PAN_DIRECTION_DOWN = 3;
+
+    @IntDef({PAN_DIRECTION_LEFT, PAN_DIRECTION_RIGHT, PAN_DIRECTION_UP, PAN_DIRECTION_DOWN})
+    public @interface PanDirection {
+    }
+
     /**
      * A callback to inform the magnification transition result on the given display.
      */
@@ -188,6 +204,87 @@
         }
     }
 
+    /**
+     * An interface to configure how much the magnification center should be affected when panning
+     * in steps.
+     */
+    public interface MagnificationPanStepProvider {
+        /**
+         * Calculate the next value based on the current scale.
+         *
+         * @param currentScale The current magnification scale value.
+         * @param displayId The displayId for the display being magnified.
+         * @return The next pan step value.
+         */
+        float nextPanStep(float currentScale, int displayId);
+    }
+
+    public static class DefaultMagnificationPanStepProvider implements
+            MagnificationPanStepProvider, DisplayManager.DisplayListener {
+        // We want panning to be 40 dip per keystroke at scale 2, and 1 dip per keystroke at scale
+        // 20. This can be defined using y = mx + b to get the slope and intercept.
+        // This works even if the device does not allow magnification up to 20x; we will still get
+        // a reasonable lineary ramp of panning movement for each scale step.
+        private static final float DEFAULT_SCALE = 2.0f;
+        private static final float PAN_STEP_AT_DEFAULT_SCALE_DIP = 40.0f;
+        private static final float SCALE_FOR_1_DIP_PAN = 20.0f;
+
+        private SparseDoubleArray mPanStepSlopes;
+        private SparseDoubleArray mPanStepIntercepts;
+
+        private final DisplayManager mDisplayManager;
+
+        DefaultMagnificationPanStepProvider(Context context) {
+            mDisplayManager = context.getSystemService(DisplayManager.class);
+            mDisplayManager.registerDisplayListener(this, /*handler=*/null);
+            mPanStepSlopes = new SparseDoubleArray();
+            mPanStepIntercepts = new SparseDoubleArray();
+        }
+
+        @Override
+        public void onDisplayAdded(int displayId) {
+            updateForDisplay(displayId);
+        }
+
+        @Override
+        public void onDisplayChanged(int displayId) {
+            updateForDisplay(displayId);
+        }
+
+        @Override
+        public void onDisplayRemoved(int displayId) {
+            mPanStepSlopes.delete(displayId);
+            mPanStepIntercepts.delete(displayId);
+        }
+
+        @Override
+        public float nextPanStep(float currentScale, int displayId) {
+            if (mPanStepSlopes.indexOfKey(displayId) < 0) {
+                updateForDisplay(displayId);
+            }
+            return Math.max((float) (mPanStepSlopes.get(displayId) * currentScale
+                    + mPanStepIntercepts.get(displayId)), 1);
+        }
+
+        private void updateForDisplay(int displayId) {
+            Display display = mDisplayManager.getDisplay(displayId);
+            if (display == null) {
+                return;
+            }
+            DisplayMetrics metrics = new DisplayMetrics();
+            display.getMetrics(metrics);
+            final float panStepAtDefaultScaleInPx = TypedValue.convertDimensionToPixels(
+                    TypedValue.COMPLEX_UNIT_DIP, PAN_STEP_AT_DEFAULT_SCALE_DIP, metrics);
+            final float panStepAtMaxScaleInPx = TypedValue.convertDimensionToPixels(
+                    TypedValue.COMPLEX_UNIT_DIP, 1.0f, metrics);
+            final float panStepSlope = (panStepAtMaxScaleInPx - panStepAtDefaultScaleInPx)
+                    / (SCALE_FOR_1_DIP_PAN - DEFAULT_SCALE);
+            mPanStepSlopes.put(displayId, panStepSlope);
+            mPanStepIntercepts.put(displayId,
+                    panStepAtDefaultScaleInPx - panStepSlope * DEFAULT_SCALE);
+        }
+    }
+
     public MagnificationController(AccessibilityManagerService ams, Object lock,
             Context context, MagnificationScaleProvider scaleProvider,
             Executor backgroundExecutor) {
@@ -201,6 +298,7 @@
         mSupportWindowMagnification = context.getPackageManager().hasSystemFeature(
                 FEATURE_WINDOW_MAGNIFICATION);
         mScaleStepProvider = new DefaultMagnificationScaleStepProvider();
+        mPanStepProvider = new DefaultMagnificationPanStepProvider(mContext);
 
         mAlwaysOnMagnificationFeatureFlag = new AlwaysOnMagnificationFeatureFlag(context);
         mAlwaysOnMagnificationFeatureFlag.addOnChangedListener(
@@ -935,13 +1033,12 @@
     }
 
     /**
-     * Scales the magnifier on the given display one step in/out based on the zoomIn param.
+     * Scales the magnifier on the given display one step in/out based on the direction param.
      *
      * @param displayId The logical display id.
      * @param direction Whether the scale should be zoomed in or out.
-     * @return {@code true} if the magnification scale was affected.
      */
-    public boolean scaleMagnificationByStep(int displayId, @ZoomDirection int direction) {
+    public void scaleMagnificationByStep(int displayId, @ZoomDirection int direction) {
         if (getFullScreenMagnificationController().isActivated(displayId)) {
             final float magnificationScale = getFullScreenMagnificationController().getScale(
                     displayId);
@@ -950,7 +1047,6 @@
             getFullScreenMagnificationController().setScaleAndCenter(displayId,
                     nextMagnificationScale,
                     Float.NaN, Float.NaN, true, MAGNIFICATION_GESTURE_HANDLER_ID);
-            return nextMagnificationScale != magnificationScale;
         }
 
         if (getMagnificationConnectionManager().isWindowMagnifierEnabled(displayId)) {
@@ -959,10 +1055,51 @@
             final float nextMagnificationScale = mScaleStepProvider.nextScaleStep(
                     magnificationScale, direction);
             getMagnificationConnectionManager().setScale(displayId, nextMagnificationScale);
-            return nextMagnificationScale != magnificationScale;
+        }
+    }
+
+    /**
+     * Pans the magnifier on the given display one step left/right/up/down based on the direction
+     * param.
+     *
+     * @param displayId The logical display id.
+     * @param direction Whether the direction should be left/right/up/down.
+     */
+    public void panMagnificationByStep(int displayId, @PanDirection int direction) {
+        final boolean fullscreenActivated =
+                getFullScreenMagnificationController().isActivated(displayId);
+        final boolean windowActivated =
+                getMagnificationConnectionManager().isWindowMagnifierEnabled(displayId);
+        if (!fullscreenActivated && !windowActivated) {
+            return;
         }
 
-        return false;
+        final float scale = fullscreenActivated
+                ? getFullScreenMagnificationController().getScale(displayId)
+                        : getMagnificationConnectionManager().getScale(displayId);
+        final float step = mPanStepProvider.nextPanStep(scale, displayId);
+
+        float offsetX = 0;
+        float offsetY = 0;
+        if (direction == PAN_DIRECTION_LEFT) {
+            offsetX = -step;
+        } else if (direction == PAN_DIRECTION_RIGHT) {
+            offsetX = step;
+        } else if (direction == PAN_DIRECTION_UP) {
+            offsetY = -step;
+        } else if (direction == PAN_DIRECTION_DOWN) {
+            offsetY = step;
+        }
+
+        if (fullscreenActivated) {
+            final float centerX = getFullScreenMagnificationController().getCenterX(displayId);
+            final float centerY = getFullScreenMagnificationController().getCenterY(displayId);
+            getFullScreenMagnificationController().setScaleAndCenter(displayId, scale,
+                    centerX + offsetX, centerY + offsetY, true, MAGNIFICATION_GESTURE_HANDLER_ID);
+        } else {
+            getMagnificationConnectionManager().moveWindowMagnification(displayId, offsetX,
+                    offsetY);
+        }
     }
 
     private final class DisableMagnificationCallback implements
diff --git a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java
index 9cc5a8c..669025f 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java
@@ -159,15 +159,17 @@
                         new SafeOneTimeExecuteAppFunctionCallback.CompletionCallback() {
                             @Override
                             public void finalizeOnSuccess(
-                                    @NonNull ExecuteAppFunctionResponse result) {
+                                    @NonNull ExecuteAppFunctionResponse result,
+                                    long executionStartTimeMillis) {
                                 mLoggerWrapper.logAppFunctionSuccess(requestInternal, result,
-                                        callingUid);
+                                        callingUid, executionStartTimeMillis);
                             }
 
                             @Override
-                            public void finalizeOnError(@NonNull AppFunctionException error) {
+                            public void finalizeOnError(@NonNull AppFunctionException error,
+                                    long executionStartTimeMillis) {
                                 mLoggerWrapper.logAppFunctionError(requestInternal,
-                                        error.getErrorCode(), callingUid);
+                                        error.getErrorCode(), callingUid, executionStartTimeMillis);
                             }
                         });
 
diff --git a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionsLoggerWrapper.java b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionsLoggerWrapper.java
index b59915a..7ba1bbc 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionsLoggerWrapper.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionsLoggerWrapper.java
@@ -41,25 +41,34 @@
     }
 
     void logAppFunctionSuccess(ExecuteAppFunctionAidlRequest request,
-            ExecuteAppFunctionResponse response, int callingUid) {
+            ExecuteAppFunctionResponse response, int callingUid, long executionStartTimeMillis) {
         logAppFunctionsRequestReported(request, SUCCESS_RESPONSE_CODE,
-                response.getResponseDataSize(), callingUid);
+                response.getResponseDataSize(), callingUid, executionStartTimeMillis);
     }
 
-    void logAppFunctionError(ExecuteAppFunctionAidlRequest request, int errorCode, int callingUid) {
-        logAppFunctionsRequestReported(request, errorCode, /* responseSizeBytes = */ 0, callingUid);
+    void logAppFunctionError(ExecuteAppFunctionAidlRequest request, int errorCode, int callingUid,
+            long executionStartTimeMillis) {
+        logAppFunctionsRequestReported(request, errorCode, /* responseSizeBytes = */ 0, callingUid,
+                executionStartTimeMillis);
     }
 
     private void logAppFunctionsRequestReported(ExecuteAppFunctionAidlRequest request,
-            int errorCode, int responseSizeBytes, int callingUid) {
-        final long latency = SystemClock.elapsedRealtime() - request.getRequestTime();
+            int errorCode, int responseSizeBytes, int callingUid, long executionStartTimeMillis) {
+        final long e2eRequestLatencyMillis =
+                SystemClock.elapsedRealtime() - request.getRequestTime();
+        final long requestOverheadMillis =
+                executionStartTimeMillis > 0 ? (executionStartTimeMillis - request.getRequestTime())
+                        : e2eRequestLatencyMillis;
         LOGGING_THREAD_EXECUTOR.execute(() -> AppFunctionsStatsLog.write(
                 AppFunctionsStatsLog.APP_FUNCTIONS_REQUEST_REPORTED,
-                callingUid,
+                /* callerPackageUid= */ callingUid,
+                /* targetPackageUid= */
                 getPackageUid(request.getClientRequest().getTargetPackageName()),
-                errorCode,
-                request.getClientRequest().getRequestDataSize(), responseSizeBytes,
-                latency)
+                /* errorCode= */ errorCode,
+                /* requestSizeBytes= */ request.getClientRequest().getRequestDataSize(),
+                /* responseSizeBytes= */  responseSizeBytes,
+                /* requestDurationMs= */ e2eRequestLatencyMillis,
+                /* requestOverheadMs= */ requestOverheadMillis)
         );
     }
 
diff --git a/services/appfunctions/java/com/android/server/appfunctions/RunAppFunctionServiceCallback.java b/services/appfunctions/java/com/android/server/appfunctions/RunAppFunctionServiceCallback.java
index 896c0fc..a5ae7e3 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/RunAppFunctionServiceCallback.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/RunAppFunctionServiceCallback.java
@@ -52,6 +52,7 @@
             @NonNull IAppFunctionService service,
             @NonNull ServiceUsageCompleteListener serviceUsageCompleteListener) {
         try {
+            mSafeExecuteAppFunctionCallback.setExecutionStartTimeMillis();
             service.executeAppFunction(
                     mRequestInternal.getClientRequest(),
                     mRequestInternal.getCallingPackage(),
diff --git a/services/art-wear-profile b/services/art-wear-profile
index 1e3090f..f080715 100644
--- a/services/art-wear-profile
+++ b/services/art-wear-profile
@@ -7419,7 +7419,7 @@
 PLcom/android/server/app/GameManagerService;->updateConfigsForUser(IZ[Ljava/lang/String;)V
 PLcom/android/server/app/GameManagerService;->writeGameModeInterventionsToFile(I)V
 PLcom/android/server/app/GameManagerSettings;-><init>(Ljava/io/File;)V
-HPLcom/android/server/app/GameManagerSettings;->getConfigOverride(Ljava/lang/String;)Lcom/android/server/app/GameManagerService$GamePackageConfiguration;
+HPLcom/android/server/app/GameManagerSettings;->getConfigOverrideLocked(Ljava/lang/String;)Lcom/android/server/app/GameManagerService$GamePackageConfiguration;
 HPLcom/android/server/app/GameManagerSettings;->getGameModeLocked(Ljava/lang/String;)I
 PLcom/android/server/app/GameManagerSettings;->readPersistentDataLocked()Z
 PLcom/android/server/appbinding/AppBindingConstants;-><init>(Ljava/lang/String;)V
diff --git a/services/autofill/bugfixes.aconfig b/services/autofill/bugfixes.aconfig
index 9c83757..ec6c3b7 100644
--- a/services/autofill/bugfixes.aconfig
+++ b/services/autofill/bugfixes.aconfig
@@ -74,6 +74,16 @@
 }
 
 flag {
+  name: "multiple_fill_history"
+  namespace: "autofill"
+  description: "Allows tracking per Session FillEventHistory. As a bugfix flag to guard against DeviceConfig flag"
+  bug: "365630157"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
+
+flag {
   name: "add_session_id_to_client_state"
   namespace: "autofill"
   description: "Include the session id into the FillEventHistory events as part of ClientState"
@@ -86,3 +96,23 @@
   description: "Highlight single field after autofill selection"
   bug: "41496744"
 }
+
+flag {
+  name: "metrics_fixes"
+  namespace: "autofill"
+  description: "Fixes various framework reported metrics"
+  bug: "362581326, 363011343"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
+
+flag {
+  name: "add_accessibility_title_for_augmented_autofill_dropdown"
+  namespace: "autofill"
+  description: "Add accessibility title for augmented autofill dropdown"
+  bug: "375284244"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 0fa43ac..11710c9 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -152,6 +152,15 @@
     @GuardedBy("mLock")
     private final SparseArray<Session> mSessions = new SparseArray<>();
 
+    /**
+     * Cache of FillEventHistory for active Sessions.
+     *
+     * <p>New histories are added whenever a Session is created and are kept until Sessions are
+     * removed through removeSessionLocked()
+     */
+    @GuardedBy("mLock")
+    private final SparseArray<FillEventHistory> mFillHistories = new SparseArray<>();
+
     /** The last selection */
     @GuardedBy("mLock")
     private FillEventHistory mEventHistory;
@@ -663,6 +672,10 @@
                 flags, mInputMethodManagerInternal, isPrimaryCredential);
         mSessions.put(newSession.id, newSession);
 
+        if (Flags.multipleFillHistory() && !forAugmentedAutofillOnly) {
+            mFillHistories.put(newSession.id, new FillEventHistory(sessionId, null));
+        }
+
         return newSession;
     }
 
@@ -756,6 +769,28 @@
                         TAG,
                         "removeSessionLocked(): removed " + sessionId);
             }
+
+            FillEventHistory history = null;
+
+            if (Flags.multipleFillHistory() && mFillHistories != null) {
+                history = mFillHistories.get(sessionId);
+                mFillHistories.delete(sessionId);
+            }
+
+            if (mInfo == null || mInfo.getServiceInfo() == null) {
+                if (sVerbose) {
+                    Slog.v(TAG, "removeSessionLocked(): early return because mInfo is null");
+                }
+                return;
+            }
+
+            if (mMaster == null) {
+                if (sVerbose) {
+                    Slog.v(TAG, "removeSessionLocked(): early return because mMaster is null");
+                }
+                return;
+            }
+
             RemoteFillService remoteService =
                     new RemoteFillService(
                             getContext(),
@@ -764,7 +799,8 @@
                             /* callbacks= */ null,
                             mMaster.isInstantServiceAllowed(),
                             /* credentialAutofillService= */ null);
-            remoteService.onSessionDestroyed(null);
+
+            remoteService.onSessionDestroyed(history);
         }
     }
 
@@ -886,6 +922,10 @@
             }
         }
         mSessions.clear();
+        if (Flags.multipleFillHistory()) {
+            mFillHistories.clear();
+        }
+
         for (int i = 0; i < remoteFillServices.size(); i++) {
             remoteFillServices.valueAt(i).destroy();
         }
@@ -944,60 +984,132 @@
         return true;
     }
 
-    /**
-     * Updates the last fill selection when an authentication was selected.
-     */
-    void setAuthenticationSelected(int sessionId, @Nullable Bundle clientState,
-            int uiType, @Nullable AutofillId focusedId) {
-        synchronized (mLock) {
-            if (isValidEventLocked("setAuthenticationSelected()", sessionId)) {
-                mEventHistory.addEvent(
-                        new Event(Event.TYPE_AUTHENTICATION_SELECTED, null, clientState, null, null,
-                                null, null, null, null, null, null,
-                                NO_SAVE_UI_REASON_NONE, uiType, focusedId));
+    @GuardedBy("mLock")
+    void addEventToHistory(String eventName, int sessionId, Event event) {
+        // For the singleton filleventhistory
+        if (isValidEventLocked(eventName, sessionId)) {
+            mEventHistory.addEvent(event);
+        }
+
+        if (Flags.multipleFillHistory()) {
+            FillEventHistory history = mFillHistories.get(sessionId);
+            if (history != null) {
+                history.addEvent(event);
+            } else {
+                if (sVerbose) {
+                    Slog.v(TAG, eventName
+                            + " not logged because FillEventHistory is not tracked for: "
+                            + sessionId);
+                }
             }
         }
     }
 
     /**
-     * Updates the last fill selection when an dataset authentication was selected.
+     * Updates the last fill selection when an authentication was selected.
      */
-    void logDatasetAuthenticationSelected(@Nullable String selectedDataset, int sessionId,
-            @Nullable Bundle clientState, int uiType, @Nullable AutofillId focusedId) {
+    void setAuthenticationSelected(int sessionId, @Nullable Bundle clientState,
+            int uiType, @Nullable AutofillId focusedId, boolean shouldAdd) {
         synchronized (mLock) {
-            if (isValidEventLocked("logDatasetAuthenticationSelected()", sessionId)) {
-                mEventHistory.addEvent(
-                        new Event(Event.TYPE_DATASET_AUTHENTICATION_SELECTED, selectedDataset,
-                                clientState, null, null, null, null, null, null, null, null,
-                                NO_SAVE_UI_REASON_NONE, uiType, focusedId));
+
+            String methodName = "setAuthenticationSelected()";
+
+            if (!shouldAdd) {
+                if (sVerbose) {
+                    Slog.v(TAG, methodName + " not logged because shouldAdd is false");
+                }
+                return;
             }
+
+            Event event =
+                    new Event(
+                            Event.TYPE_AUTHENTICATION_SELECTED,
+                            null,
+                            clientState,
+                            null,
+                            null,
+                            null,
+                            null,
+                            null,
+                            null,
+                            null,
+                            null,
+                            NO_SAVE_UI_REASON_NONE,
+                            uiType,
+                            focusedId);
+
+            addEventToHistory(methodName, sessionId, event);
+        }
+    }
+
+    /** Updates the last fill selection when a dataset authentication was selected. */
+    void logDatasetAuthenticationSelected(
+            @Nullable String selectedDataset,
+            int sessionId,
+            @Nullable Bundle clientState,
+            int uiType,
+            @Nullable AutofillId focusedId,
+            boolean shouldAdd) {
+        synchronized (mLock) {
+            String methodName = "logDatasetAuthenticationSelected()";
+
+            if (!shouldAdd) {
+                if (sVerbose) {
+                    Slog.v(TAG, methodName + " not logged because shouldAdd is false");
+                }
+                return;
+            }
+
+            Event event = new Event(Event.TYPE_DATASET_AUTHENTICATION_SELECTED, selectedDataset,
+                                clientState, null, null, null, null, null, null, null, null,
+                                NO_SAVE_UI_REASON_NONE, uiType, focusedId);
+            addEventToHistory(methodName, sessionId, event);
         }
     }
 
     /**
      * Updates the last fill selection when an save Ui is shown.
      */
-    void logSaveShown(int sessionId, @Nullable Bundle clientState) {
+    void logSaveShown(int sessionId, @Nullable Bundle clientState, boolean shouldAdd) {
         synchronized (mLock) {
-            if (isValidEventLocked("logSaveShown()", sessionId)) {
-                mEventHistory.addEvent(new Event(Event.TYPE_SAVE_SHOWN, null, clientState, null,
-                        null, null, null, null, null, null, null, /* focusedId= */ null));
+            String methodName = "logSaveShown()";
+
+            if (!shouldAdd) {
+                if (sVerbose) {
+                    Slog.v(TAG, methodName + " not logged because shouldAdd is false");
+                }
+                return;
             }
+
+            Event event = new Event(Event.TYPE_SAVE_SHOWN, null, clientState, null,
+                        null, null, null, null, null, null, null, /* focusedId= */ null);
+
+            addEventToHistory(methodName, sessionId, event);
         }
     }
 
-    /**
-     * Updates the last fill response when a dataset was selected.
-     */
-    void logDatasetSelected(@Nullable String selectedDataset, int sessionId,
-            @Nullable Bundle clientState,  int uiType, @Nullable AutofillId focusedId) {
+    /** Updates the last fill response when a dataset was selected. */
+    void logDatasetSelected(
+            @Nullable String selectedDataset,
+            int sessionId,
+            @Nullable Bundle clientState,
+            int uiType,
+            @Nullable AutofillId focusedId,
+            boolean shouldAdd) {
         synchronized (mLock) {
-            if (isValidEventLocked("logDatasetSelected()", sessionId)) {
-                mEventHistory.addEvent(
-                        new Event(Event.TYPE_DATASET_SELECTED, selectedDataset, clientState, null,
-                                null, null, null, null, null, null, null, NO_SAVE_UI_REASON_NONE,
-                                uiType, focusedId));
+            String methodName = "logDatasetSelected()";
+
+            if (!shouldAdd) {
+                if (sVerbose) {
+                    Slog.v(TAG, methodName + " not logged because shouldAdd is false");
+                }
+                return;
             }
+
+            Event event = new Event(Event.TYPE_DATASET_SELECTED, selectedDataset, clientState, null,
+                                null, null, null, null, null, null, null, NO_SAVE_UI_REASON_NONE,
+                                uiType, focusedId);
+            addEventToHistory(methodName, sessionId, event);
         }
     }
 
@@ -1005,40 +1117,75 @@
      * Updates the last fill response when a dataset is shown.
      */
     void logDatasetShown(int sessionId, @Nullable Bundle clientState, int uiType,
-            @Nullable AutofillId focusedId) {
+            @Nullable AutofillId focusedId, boolean shouldAdd) {
         synchronized (mLock) {
-            if (isValidEventLocked("logDatasetShown", sessionId)) {
-                mEventHistory.addEvent(
-                        new Event(Event.TYPE_DATASETS_SHOWN, null, clientState, null, null, null,
+            String methodName = "logDatasetShown()";
+
+            if (!shouldAdd) {
+                if (sVerbose) {
+                    Slog.v(TAG, methodName + " not logged because shouldAdd is false");
+                }
+                return;
+            }
+
+            Event event = new Event(Event.TYPE_DATASETS_SHOWN, null, clientState, null, null, null,
                                 null, null, null, null, null, NO_SAVE_UI_REASON_NONE,
-                                uiType, focusedId));
+                                uiType, focusedId);
+            addEventToHistory(methodName, sessionId, event);
+        }
+    }
+
+    void logViewEnteredForHistory(
+            int sessionId,
+            @Nullable Bundle clientState,
+            FillEventHistory history,
+            @Nullable AutofillId focusedId) {
+        if (history.getEvents() != null) {
+            // Do not log this event more than once
+            for (Event event : history.getEvents()) {
+                if (event.getType() == Event.TYPE_VIEW_REQUESTED_AUTOFILL) {
+                    if (sVerbose) {
+                        Slog.v(TAG, "logViewEntered: already logged TYPE_VIEW_REQUESTED_AUTOFILL");
+                    }
+                    return;
+                }
             }
         }
+
+        history.addEvent(
+                new Event(Event.TYPE_VIEW_REQUESTED_AUTOFILL, null, clientState, null,
+                        null, null, null, null, null, null, null, focusedId));
     }
 
     /**
      * Updates the last fill response when a view was entered.
      */
     void logViewEntered(int sessionId, @Nullable Bundle clientState,
-            @Nullable AutofillId focusedId) {
+            @Nullable AutofillId focusedId, boolean shouldAdd) {
         synchronized (mLock) {
-            if (!isValidEventLocked("logViewEntered", sessionId)) {
+            String methodName = "logViewEntered()";
+
+            if (!shouldAdd) {
+                if (sVerbose) {
+                    Slog.v(TAG, methodName + " not logged because shouldAdd is false");
+                }
                 return;
             }
 
-            if (mEventHistory.getEvents() != null) {
-                // Do not log this event more than once
-                for (Event event : mEventHistory.getEvents()) {
-                    if (event.getType() == Event.TYPE_VIEW_REQUESTED_AUTOFILL) {
-                        Slog.v(TAG, "logViewEntered: already logged TYPE_VIEW_REQUESTED_AUTOFILL");
-                        return;
-                    }
-                }
+            // This log does not call addEventToHistory() because each distinct FillEventHistory
+            // can only contain 1 TYPE_VIEW_REQUESTED_AUTOFILL event. Therefore, checking both
+            // the singleton FillEventHistory and the per Session FillEventHistory is necessary
+
+            if (isValidEventLocked(methodName, sessionId)) {
+                logViewEnteredForHistory(sessionId, clientState, mEventHistory, focusedId);
             }
 
-            mEventHistory.addEvent(
-                    new Event(Event.TYPE_VIEW_REQUESTED_AUTOFILL, null, clientState, null,
-                            null, null, null, null, null, null, null, focusedId));
+            if (Flags.multipleFillHistory()) {
+                FillEventHistory history = mFillHistories.get(sessionId);
+                if (history != null) {
+                    logViewEnteredForHistory(sessionId, clientState, history, focusedId);
+                }
+            }
         }
     }
 
@@ -1096,12 +1243,12 @@
             @Nullable ArrayList<String> changedDatasetIds,
             @Nullable ArrayList<AutofillId> manuallyFilledFieldIds,
             @Nullable ArrayList<ArrayList<String>> manuallyFilledDatasetIds,
-            @NonNull ComponentName appComponentName, boolean compatMode) {
+            @NonNull ComponentName appComponentName, boolean compatMode, boolean shouldAdd) {
         logContextCommittedLocked(sessionId, clientState, selectedDatasets, ignoredDatasets,
                 changedFieldIds, changedDatasetIds, manuallyFilledFieldIds,
                 manuallyFilledDatasetIds, /* detectedFieldIdsList= */ null,
                 /* detectedFieldClassificationsList= */ null, appComponentName, compatMode,
-                Event.NO_SAVE_UI_REASON_NONE);
+                Event.NO_SAVE_UI_REASON_NONE, shouldAdd);
     }
 
     @GuardedBy("mLock")
@@ -1115,9 +1262,19 @@
             @Nullable ArrayList<AutofillId> detectedFieldIdsList,
             @Nullable ArrayList<FieldClassification> detectedFieldClassificationsList,
             @NonNull ComponentName appComponentName, boolean compatMode,
-            @NoSaveReason int saveDialogNotShowReason) {
-        if (isValidEventLocked("logDatasetNotSelected()", sessionId)) {
+            @NoSaveReason int saveDialogNotShowReason,
+            boolean shouldAdd) {
+
+        String methodName = "logContextCommittedLocked()";
+
+        if (!shouldAdd) {
             if (sVerbose) {
+                Slog.v(TAG, methodName + " not logged because shouldAdd is false");
+            }
+            return;
+        }
+
+        if (sVerbose) {
                 Slog.v(TAG, "logContextCommitted() with FieldClassification: id=" + sessionId
                         + ", selectedDatasets=" + selectedDatasets
                         + ", ignoredDatasetIds=" + ignoredDatasets
@@ -1129,44 +1286,58 @@
                         + ", appComponentName=" + appComponentName.toShortString()
                         + ", compatMode=" + compatMode
                         + ", saveDialogNotShowReason=" + saveDialogNotShowReason);
-            }
-            AutofillId[] detectedFieldsIds = null;
-            FieldClassification[] detectedFieldClassifications = null;
-            if (detectedFieldIdsList != null) {
-                detectedFieldsIds = new AutofillId[detectedFieldIdsList.size()];
-                detectedFieldIdsList.toArray(detectedFieldsIds);
-                detectedFieldClassifications =
-                        new FieldClassification[detectedFieldClassificationsList.size()];
-                detectedFieldClassificationsList.toArray(detectedFieldClassifications);
-
-                final int numberFields = detectedFieldsIds.length;
-                int totalSize = 0;
-                float totalScore = 0;
-                for (int i = 0; i < numberFields; i++) {
-                    final FieldClassification fc = detectedFieldClassifications[i];
-                    final List<Match> matches = fc.getMatches();
-                    final int size = matches.size();
-                    totalSize += size;
-                    for (int j = 0; j < size; j++) {
-                        totalScore += matches.get(j).getScore();
-                    }
-                }
-
-                final int averageScore = (int) ((totalScore * 100) / totalSize);
-                mMetricsLogger.write(Helper
-                        .newLogMaker(MetricsEvent.AUTOFILL_FIELD_CLASSIFICATION_MATCHES,
-                                appComponentName, getServicePackageName(), sessionId, compatMode)
-                        .setCounterValue(numberFields)
-                        .addTaggedData(MetricsEvent.FIELD_AUTOFILL_MATCH_SCORE,
-                                averageScore));
-            }
-            mEventHistory.addEvent(new Event(Event.TYPE_CONTEXT_COMMITTED, null,
-                    clientState, selectedDatasets, ignoredDatasets,
-                    changedFieldIds, changedDatasetIds,
-                    manuallyFilledFieldIds, manuallyFilledDatasetIds,
-                    detectedFieldsIds, detectedFieldClassifications, saveDialogNotShowReason,
-                    /* focusedId= */ null));
         }
+
+        AutofillId[] detectedFieldsIds = null;
+        FieldClassification[] detectedFieldClassifications = null;
+        if (detectedFieldIdsList != null) {
+            detectedFieldsIds = new AutofillId[detectedFieldIdsList.size()];
+            detectedFieldIdsList.toArray(detectedFieldsIds);
+            detectedFieldClassifications =
+                    new FieldClassification[detectedFieldClassificationsList.size()];
+            detectedFieldClassificationsList.toArray(detectedFieldClassifications);
+
+            final int numberFields = detectedFieldsIds.length;
+            int totalSize = 0;
+            float totalScore = 0;
+            for (int i = 0; i < numberFields; i++) {
+                final FieldClassification fc = detectedFieldClassifications[i];
+                final List<Match> matches = fc.getMatches();
+                final int size = matches.size();
+                totalSize += size;
+                for (int j = 0; j < size; j++) {
+                    totalScore += matches.get(j).getScore();
+                }
+            }
+
+            final int averageScore = (int) ((totalScore * 100) / totalSize);
+            mMetricsLogger.write(
+                    Helper.newLogMaker(
+                                    MetricsEvent.AUTOFILL_FIELD_CLASSIFICATION_MATCHES,
+                                    appComponentName,
+                                    getServicePackageName(),
+                                    sessionId,
+                                    compatMode)
+                            .setCounterValue(numberFields)
+                            .addTaggedData(MetricsEvent.FIELD_AUTOFILL_MATCH_SCORE, averageScore));
+        }
+        Event event =
+                new Event(
+                        Event.TYPE_CONTEXT_COMMITTED,
+                        null,
+                        clientState,
+                        selectedDatasets,
+                        ignoredDatasets,
+                        changedFieldIds,
+                        changedDatasetIds,
+                        manuallyFilledFieldIds,
+                        manuallyFilledDatasetIds,
+                        detectedFieldsIds,
+                        detectedFieldClassifications,
+                        saveDialogNotShowReason,
+                        /* focusedId= */ null);
+
+        addEventToHistory(methodName, sessionId, event);
     }
 
     /**
@@ -1174,7 +1345,9 @@
      *
      * @param callingUid The calling uid
      * @return The history for the autofill or the augmented autofill events depending on the {@code
-     * callingUid}, or {@code null} if there is none.
+     *     callingUid}, or {@code null} if there is none.
+     * @deprecated Use {@link
+     *     android.service.autofill.AutofillService#onSessionDestroyed(FillEventHistory)}
      */
     FillEventHistory getFillEventHistory(int callingUid) {
         synchronized (mLock) {
diff --git a/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java b/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java
index bd1b0ea..6ccf5e4 100644
--- a/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java
+++ b/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java
@@ -45,6 +45,7 @@
 import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_NO_FOCUS;
 import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_REQUEST_TIMEOUT;
 import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_SESSION_COMMITTED_PREMATURELY;
+import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_SUGGESTION_FILTER_OUT;
 import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_UNKNOWN_REASON;
 import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_VIEW_CHANGED;
 import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_VIEW_FOCUSED_BEFORE_FILL_DIALOG_RESPONSE;
@@ -98,6 +99,7 @@
             NOT_SHOWN_REASON_REQUEST_FAILED,
             NOT_SHOWN_REASON_NO_FOCUS,
             NOT_SHOWN_REASON_SESSION_COMMITTED_PREMATURELY,
+            NOT_SHOWN_REASON_SUGGESTION_FILTERED,
             NOT_SHOWN_REASON_UNKNOWN
     })
     @Retention(RetentionPolicy.SOURCE)
@@ -178,6 +180,8 @@
             AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_SESSION_COMMITTED_PREMATURELY;
     public static final int NOT_SHOWN_REASON_UNKNOWN =
             AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_UNKNOWN_REASON;
+    public static final int NOT_SHOWN_REASON_SUGGESTION_FILTERED =
+            AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_SUGGESTION_FILTER_OUT;
 
     public static final int AUTHENTICATION_TYPE_UNKNOWN =
             AUTOFILL_PRESENTATION_EVENT_REPORTED__AUTHENTICATION_TYPE__AUTHENTICATION_TYPE_UNKNOWN;
@@ -286,12 +290,43 @@
         });
     }
 
+    /**
+     * Call this when first entering the View. It will check if there are pre-existing characters
+     * in the view, and sets NOT_SHOWN_REASON_SUGGESTION_FILTERED if there is
+     */
+    public void maybeSetNoPresentationEventReasonSuggestionsFiltered(AutofillValue value) {
+        mEventInternal.ifPresent(
+                event -> {
+                    if (value == null || !value.isText()) {
+                        return;
+                    }
+
+                    int length = value.getTextValue().length();
+
+                    if (length > 0) {
+                        maybeSetNoPresentationEventReason(NOT_SHOWN_REASON_SUGGESTION_FILTERED);
+                    }
+                });
+    }
+
     public void maybeSetNoPresentationEventReasonIfNoReasonExists(@NotShownReason int reason) {
-        mEventInternal.ifPresent(event -> {
-            if (event.mCountShown == 0 && event.mNoPresentationReason == NOT_SHOWN_REASON_UNKNOWN) {
-                event.mNoPresentationReason = reason;
-            }
-        });
+        mEventInternal.ifPresent(
+                event -> {
+                    if (event.mCountShown != 0) {
+                        return;
+                    }
+
+                    // The only events that can be overwritten.
+                    // NOT_SHOWN_REASON_UNKNOWN is the default for inline/dropdown
+                    // NOT_SHOWN_REASON_NO_FOCUS is the default for fill dialog
+                    if (event.mNoPresentationReason != NOT_SHOWN_REASON_UNKNOWN
+                            || event.mNoPresentationReason != NOT_SHOWN_REASON_NO_FOCUS) {
+                        Slog.d(TAG, "Not setting no presentation reason because it already exists");
+                        return;
+                    }
+
+                    event.mNoPresentationReason = reason;
+                });
     }
 
     public void maybeSetAvailableCount(@Nullable List<Dataset> datasetList,
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 9c6e4741..3ecff3b 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -41,6 +41,7 @@
 import static android.service.autofill.FillRequest.INVALID_REQUEST_ID;
 import static android.service.autofill.Flags.highlightAutofillSingleField;
 import static android.service.autofill.Flags.improveFillDialogAconfig;
+import static android.service.autofill.Flags.metricsFixes;
 import static android.view.autofill.AutofillManager.ACTION_RESPONSE_EXPIRED;
 import static android.view.autofill.AutofillManager.ACTION_START_SESSION;
 import static android.view.autofill.AutofillManager.ACTION_VALUE_CHANGED;
@@ -1961,7 +1962,7 @@
 
             if (mLogViewEntered) {
                 mLogViewEntered = false;
-                mService.logViewEntered(id, null, mCurrentViewId);
+                mService.logViewEntered(id, null, mCurrentViewId, shouldAddEventToHistory());
             }
         }
 
@@ -2865,7 +2866,12 @@
                 forceRemoveFromServiceLocked();
                 return;
             }
-            mService.setAuthenticationSelected(id, mClientState, uiType, mCurrentViewId);
+            mService.setAuthenticationSelected(
+                    id,
+                    mClientState,
+                    uiType,
+                    mCurrentViewId,
+                    shouldAddEventToHistory());
         }
 
 
@@ -2940,7 +2946,12 @@
                 if (!mLoggedInlineDatasetShown) {
                     // Chip inflation already logged, do not log again.
                     // This is needed because every chip inflation will call this.
-                    mService.logDatasetShown(this.id, mClientState, uiType, mCurrentViewId);
+                    mService.logDatasetShown(
+                            this.id,
+                            mClientState,
+                            uiType,
+                            mCurrentViewId,
+                            shouldAddEventToHistory());
                     Slog.d(TAG, "onShown(): " + uiType + ", " + numDatasetsShown);
                 }
                 mLoggedInlineDatasetShown = true;
@@ -2948,7 +2959,12 @@
                 mPresentationStatsEventLogger.logWhenDatasetShown(numDatasetsShown);
                 // Explicitly sets maybeSetSuggestionPresentedTimestampMs
                 mPresentationStatsEventLogger.maybeSetSuggestionPresentedTimestampMs();
-                mService.logDatasetShown(this.id, mClientState, uiType, mCurrentViewId);
+                mService.logDatasetShown(
+                        this.id,
+                        mClientState,
+                        uiType,
+                        mCurrentViewId,
+                        shouldAddEventToHistory());
                 Slog.d(TAG, "onShown(): " + uiType + ", " + numDatasetsShown);
             }
         }
@@ -3741,8 +3757,13 @@
         final FillResponse lastResponse = getLastResponseLocked("logContextCommited(%s)");
         if (lastResponse == null) return;
 
-        mPresentationStatsEventLogger.maybeSetNoPresentationEventReason(
-                PresentationStatsEventLogger.getNoPresentationEventReason(commitReason));
+        if (metricsFixes()) {
+            mPresentationStatsEventLogger.maybeSetNoPresentationEventReasonIfNoReasonExists(
+                    PresentationStatsEventLogger.getNoPresentationEventReason(commitReason));
+        } else {
+            mPresentationStatsEventLogger.maybeSetNoPresentationEventReason(
+                    PresentationStatsEventLogger.getNoPresentationEventReason(commitReason));
+        }
         mPresentationStatsEventLogger.logAndEndEvent("Context committed");
 
         final int flags = lastResponse.getFlags();
@@ -3937,7 +3958,8 @@
                 detectedFieldClassifications,
                 mComponentName,
                 mCompatMode,
-                saveDialogNotShowReason);
+                saveDialogNotShowReason,
+                shouldAddEventToHistory());
         mSessionCommittedEventLogger.maybeSetCommitReason(commitReason);
         mSessionCommittedEventLogger.maybeSetRequestCount(mRequestCount);
         mSaveEventLogger.maybeSetSaveUiNotShownReason(saveDialogNotShowReason);
@@ -4584,7 +4606,7 @@
     }
 
     private void logSaveShown() {
-        mService.logSaveShown(id, mClientState);
+        mService.logSaveShown(id, mClientState, shouldAddEventToHistory());
     }
 
     @Nullable
@@ -5125,6 +5147,13 @@
                     mPreviouslyFillDialogPotentiallyStarted = false;
                 } else {
                     mPreviouslyFillDialogPotentiallyStarted = true;
+                    if (metricsFixes()) {
+                        // Set the default reason for now if the user doesn't trigger any focus
+                        // event on the autofillable view. This can be changed downstream when
+                        // more information is available or session is committed.
+                        mPresentationStatsEventLogger.maybeSetNoPresentationEventReason(
+                                NOT_SHOWN_REASON_NO_FOCUS);
+                    }
                 }
                 Optional<Integer> maybeRequestId =
                         requestNewFillResponseLocked(
@@ -5235,7 +5264,8 @@
                         // so this calling logViewEntered will be a nop.
                         // Calling logViewEntered() twice will only log it once
                         // TODO(271181979): this is broken for multiple partitions
-                        mService.logViewEntered(this.id, null, mCurrentViewId);
+                        mService.logViewEntered(
+                                this.id, null, mCurrentViewId, shouldAddEventToHistory());
                     }
 
                     // If this is the first time view is entered for inline, the last
@@ -5291,6 +5321,10 @@
                     if (maybeNewRequestId.isPresent()) {
                         mPresentationStatsEventLogger.maybeSetRequestId(maybeNewRequestId.get());
                     }
+                    if (metricsFixes()) {
+                        mPresentationStatsEventLogger
+                                .maybeSetNoPresentationEventReasonSuggestionsFiltered(value);
+                    }
                 }
 
                 logPresentationStatsOnViewEnteredLocked(
@@ -5325,8 +5359,14 @@
 
                     // It's not necessary that there's no more presentation for this view. It could
                     // be that the user chose some suggestion, in which case, view exits.
-                    mPresentationStatsEventLogger.maybeSetNoPresentationEventReason(
-                            NOT_SHOWN_REASON_VIEW_FOCUS_CHANGED);
+                    if (metricsFixes()) {
+                        mPresentationStatsEventLogger
+                                .maybeSetNoPresentationEventReasonIfNoReasonExists(
+                                        NOT_SHOWN_REASON_VIEW_FOCUS_CHANGED);
+                    } else {
+                        mPresentationStatsEventLogger.maybeSetNoPresentationEventReason(
+                                NOT_SHOWN_REASON_VIEW_FOCUS_CHANGED);
+                    }
                 }
                 break;
             default:
@@ -6840,8 +6880,13 @@
             // Autofill it directly...
             if (dataset.getAuthentication() == null) {
                 if (generateEvent) {
-                    mService.logDatasetSelected(dataset.getId(), id, mClientState, uiType,
-                            mCurrentViewId);
+                    mService.logDatasetSelected(
+                            dataset.getId(),
+                            id,
+                            mClientState,
+                            uiType,
+                            mCurrentViewId,
+                            shouldAddEventToHistory());
                 }
                 if (mCurrentViewId != null) {
                     mInlineSessionController.hideInlineSuggestionsUiLocked(mCurrentViewId);
@@ -6852,7 +6897,7 @@
 
             // ...or handle authentication.
             mService.logDatasetAuthenticationSelected(dataset.getId(), id, mClientState, uiType,
-                        mCurrentViewId);
+                        mCurrentViewId, shouldAddEventToHistory());
             mPresentationStatsEventLogger.maybeSetAuthenticationType(
                     AUTHENTICATION_TYPE_DATASET_AUTHENTICATION);
             // does not matter the value of isPrimary because null response won't be overridden.
@@ -6935,11 +6980,15 @@
     private void startNewEventForPresentationStatsEventLogger() {
         synchronized (mLock) {
             mPresentationStatsEventLogger.startNewEvent();
-            // Set the default reason for now if the user doesn't trigger any focus event
-            // on the autofillable view. This can be changed downstream when more
-            // information is available or session is committed.
-            mPresentationStatsEventLogger.maybeSetNoPresentationEventReason(
-                    NOT_SHOWN_REASON_NO_FOCUS);
+            // This is a fill dialog only state, moved to when we set
+            // mPreviouslyFillDialogPotentiallyStarted = true
+            if (!metricsFixes()) {
+                // Set the default reason for now if the user doesn't trigger any focus event
+                // on the autofillable view. This can be changed downstream when more
+                // information is available or session is committed.
+                mPresentationStatsEventLogger.maybeSetNoPresentationEventReason(
+                        NOT_SHOWN_REASON_NO_FOCUS);
+            }
             mPresentationStatsEventLogger.maybeSetDetectionPreference(
                     getDetectionPreferenceForLogging());
             mPresentationStatsEventLogger.maybeSetAutofillServiceUid(getAutofillServiceUid());
@@ -7696,7 +7745,11 @@
         if (sVerbose) {
             Slog.v(TAG, "logAllEvents(" + id + "): commitReason: " + val);
         }
-        mSessionCommittedEventLogger.maybeSetCommitReason(val);
+        if (metricsFixes()) {
+            mSessionCommittedEventLogger.maybeSetCommitReasonIfUnset(val);
+        } else {
+            mSessionCommittedEventLogger.maybeSetCommitReason(val);
+        }
         mSessionCommittedEventLogger.maybeSetRequestCount(mRequestCount);
         mSessionCommittedEventLogger.maybeSetSessionDurationMillis(
                 SystemClock.elapsedRealtime() - mStartTime);
@@ -7987,6 +8040,32 @@
         mService.getMaster().logRequestLocked(historyItem);
     }
 
+    /**
+     * Don't add secondary providers to FillEventHistory
+     */
+    boolean shouldAddEventToHistory() {
+
+        FillResponse lastResponse = null;
+
+        synchronized (mLock) {
+            lastResponse = getLastResponseLocked("shouldAddEventToHistory(%s)");
+        }
+
+        // There might be events (like TYPE_VIEW_REQUESTED_AUTOFILL) that are
+        // generated before FillRequest/FillResponse mechanism are started, so
+        // still need to log it
+        if (lastResponse == null) {
+            return true;
+        }
+
+        if (mRequestId.isSecondaryProvider(lastResponse.getRequestId())) {
+            // The request was to a secondary provider - don't log these events
+            return false;
+        }
+
+        return true;
+    }
+
     private void wtf(@Nullable Exception e, String fmt, Object... args) {
         final String message = String.format(fmt, args);
         synchronized (mLock) {
diff --git a/services/autofill/java/com/android/server/autofill/SessionCommittedEventLogger.java b/services/autofill/java/com/android/server/autofill/SessionCommittedEventLogger.java
index 8f3c880..7fd5648 100644
--- a/services/autofill/java/com/android/server/autofill/SessionCommittedEventLogger.java
+++ b/services/autofill/java/com/android/server/autofill/SessionCommittedEventLogger.java
@@ -76,6 +76,17 @@
     });
   }
 
+  /** Set commit_reason if not already set */
+  public void maybeSetCommitReasonIfUnset(@AutofillCommitReason int val) {
+      mEventInternal.ifPresent(
+          event -> {
+            if (event.mCommitReason != COMMIT_REASON_UNKNOWN) {
+              return;
+            }
+            event.mCommitReason = val;
+          });
+  }
+
   /**
    * Set session_duration_millis as long as mEventInternal presents.
    */
diff --git a/services/companion/java/com/android/server/companion/utils/MetricUtils.java b/services/companion/java/com/android/server/companion/utils/MetricUtils.java
index 8ea5c89..83cbde6 100644
--- a/services/companion/java/com/android/server/companion/utils/MetricUtils.java
+++ b/services/companion/java/com/android/server/companion/utils/MetricUtils.java
@@ -21,6 +21,7 @@
 import static android.companion.AssociationRequest.DEVICE_PROFILE_COMPUTER;
 import static android.companion.AssociationRequest.DEVICE_PROFILE_GLASSES;
 import static android.companion.AssociationRequest.DEVICE_PROFILE_NEARBY_DEVICE_STREAMING;
+import static android.companion.AssociationRequest.DEVICE_PROFILE_SENSOR_DEVICE_STREAMING;
 import static android.companion.AssociationRequest.DEVICE_PROFILE_WATCH;
 
 import static com.android.internal.util.FrameworkStatsLog.CDM_ASSOCIATION_ACTION;
@@ -31,6 +32,7 @@
 import static com.android.internal.util.FrameworkStatsLog.CDM_ASSOCIATION_ACTION__DEVICE_PROFILE__DEVICE_PROFILE_COMPUTER;
 import static com.android.internal.util.FrameworkStatsLog.CDM_ASSOCIATION_ACTION__DEVICE_PROFILE__DEVICE_PROFILE_GLASSES;
 import static com.android.internal.util.FrameworkStatsLog.CDM_ASSOCIATION_ACTION__DEVICE_PROFILE__DEVICE_PROFILE_NEARBY_DEVICE_STREAMING;
+import static com.android.internal.util.FrameworkStatsLog.CDM_ASSOCIATION_ACTION__DEVICE_PROFILE__DEVICE_PROFILE_SENSOR_DEVICE_STREAMING;
 import static com.android.internal.util.FrameworkStatsLog.CDM_ASSOCIATION_ACTION__DEVICE_PROFILE__DEVICE_PROFILE_NULL;
 import static com.android.internal.util.FrameworkStatsLog.CDM_ASSOCIATION_ACTION__DEVICE_PROFILE__DEVICE_PROFILE_WATCH;
 import static com.android.internal.util.FrameworkStatsLog.write;
@@ -71,6 +73,10 @@
                 DEVICE_PROFILE_NEARBY_DEVICE_STREAMING,
                 CDM_ASSOCIATION_ACTION__DEVICE_PROFILE__DEVICE_PROFILE_NEARBY_DEVICE_STREAMING
         );
+        map.put(
+                DEVICE_PROFILE_SENSOR_DEVICE_STREAMING,
+                CDM_ASSOCIATION_ACTION__DEVICE_PROFILE__DEVICE_PROFILE_SENSOR_DEVICE_STREAMING
+        );
 
         METRIC_DEVICE_PROFILE = unmodifiableMap(map);
     }
diff --git a/services/companion/java/com/android/server/companion/utils/PermissionsUtils.java b/services/companion/java/com/android/server/companion/utils/PermissionsUtils.java
index f37e0c9..6431af5 100644
--- a/services/companion/java/com/android/server/companion/utils/PermissionsUtils.java
+++ b/services/companion/java/com/android/server/companion/utils/PermissionsUtils.java
@@ -28,6 +28,7 @@
 import static android.companion.AssociationRequest.DEVICE_PROFILE_COMPUTER;
 import static android.companion.AssociationRequest.DEVICE_PROFILE_GLASSES;
 import static android.companion.AssociationRequest.DEVICE_PROFILE_NEARBY_DEVICE_STREAMING;
+import static android.companion.AssociationRequest.DEVICE_PROFILE_SENSOR_DEVICE_STREAMING;
 import static android.companion.AssociationRequest.DEVICE_PROFILE_WATCH;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.os.Binder.getCallingPid;
@@ -75,6 +76,8 @@
         map.put(DEVICE_PROFILE_GLASSES, Manifest.permission.REQUEST_COMPANION_PROFILE_GLASSES);
         map.put(DEVICE_PROFILE_NEARBY_DEVICE_STREAMING,
                 Manifest.permission.REQUEST_COMPANION_PROFILE_NEARBY_DEVICE_STREAMING);
+        map.put(DEVICE_PROFILE_SENSOR_DEVICE_STREAMING,
+                Manifest.permission.REQUEST_COMPANION_PROFILE_SENSOR_DEVICE_STREAMING);
 
         DEVICE_PROFILE_TO_PERMISSION = unmodifiableMap(map);
     }
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
index 6729231d..1f3b316 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
@@ -103,7 +103,8 @@
     private static final List<String> VIRTUAL_DEVICE_COMPANION_DEVICE_PROFILES = Arrays.asList(
             AssociationRequest.DEVICE_PROFILE_AUTOMOTIVE_PROJECTION,
             AssociationRequest.DEVICE_PROFILE_APP_STREAMING,
-            AssociationRequest.DEVICE_PROFILE_NEARBY_DEVICE_STREAMING);
+            AssociationRequest.DEVICE_PROFILE_NEARBY_DEVICE_STREAMING,
+            AssociationRequest.DEVICE_PROFILE_SENSOR_DEVICE_STREAMING);
 
     /** Enable default device camera access for apps running on virtual devices. */
     @ChangeId
@@ -738,6 +739,11 @@
         public int getDevicePolicy(int deviceId, int policyType) {
             return mImpl.getDevicePolicy(deviceId, policyType);
         }
+
+        @Override // Binder call
+        public int getDeviceIdForDisplayId(int displayId) {
+            return mImpl.getDeviceIdForDisplayId(displayId);
+        }
     }
 
     private final class LocalService extends VirtualDeviceManagerInternal {
diff --git a/services/core/Android.bp b/services/core/Android.bp
index b3d85f8..06f9e2b 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -149,6 +149,9 @@
 
         // Java/AIDL sources to be moved out to CrashRecovery module
         ":services-crashrecovery-sources",
+
+        // Indicate whether VCN is in platform or mainline
+        ":vcn-location-sources",
     ],
 
     libs: [
@@ -222,7 +225,6 @@
         "securebox",
         "apache-commons-math",
         "battery_saver_flag_lib",
-        "guava",
         "notification_flags_lib",
         "power_hint_flags_lib",
         "biometrics_flags_lib",
diff --git a/services/core/java/com/android/server/BinaryTransparencyService.java b/services/core/java/com/android/server/BinaryTransparencyService.java
index 36dff89..778c686 100644
--- a/services/core/java/com/android/server/BinaryTransparencyService.java
+++ b/services/core/java/com/android/server/BinaryTransparencyService.java
@@ -228,20 +228,31 @@
                     computePackageSignerSha256Digests(packageState.getSigningInfo());
 
             AndroidPackage pkg = packageState.getAndroidPackage();
-            for (AndroidPackageSplit split : pkg.getSplits()) {
+            if(pkg != null) {
+                for (AndroidPackageSplit split : pkg.getSplits()) {
+                    var appInfo = new IBinaryTransparencyService.AppInfo();
+                    appInfo.packageName = packageName;
+                    appInfo.longVersion = versionCode;
+                    appInfo.splitName = split.getName();  // base's split name is null
+                    // Signer digests are consistent between splits, guaranteed by Package Manager.
+                    appInfo.signerDigests = signerDigests;
+                    appInfo.mbaStatus = mbaStatus;
+
+                    // Only digest and split name are different between splits.
+                    Digest digest = measureApk(split.getPath());
+                    appInfo.digest = digest.value();
+                    appInfo.digestAlgorithm = digest.algorithm();
+
+                    results.add(appInfo);
+                }
+            } else {
+                Slog.w(TAG, packageName + " APK file is not physically present,"
+                    + " skipping split and digest measurement");
                 var appInfo = new IBinaryTransparencyService.AppInfo();
                 appInfo.packageName = packageName;
                 appInfo.longVersion = versionCode;
-                appInfo.splitName = split.getName();  // base's split name is null
-                // Signer digests are consistent between splits, guaranteed by Package Manager.
                 appInfo.signerDigests = signerDigests;
                 appInfo.mbaStatus = mbaStatus;
-
-                // Only digest and split name are different between splits.
-                Digest digest = measureApk(split.getPath());
-                appInfo.digest = digest.value();
-                appInfo.digestAlgorithm = digest.algorithm();
-
                 results.add(appInfo);
             }
 
diff --git a/services/core/java/com/android/server/GestureLauncherService.java b/services/core/java/com/android/server/GestureLauncherService.java
index ccc44a4..bedc130 100644
--- a/services/core/java/com/android/server/GestureLauncherService.java
+++ b/services/core/java/com/android/server/GestureLauncherService.java
@@ -16,9 +16,13 @@
 
 package com.android.server;
 
+import static android.service.quickaccesswallet.Flags.launchWalletOptionOnPowerDoubleTap;
+
 import static com.android.internal.R.integer.config_defaultMinEmergencyGestureTapDurationMillis;
 
 import android.app.ActivityManager;
+import android.app.ActivityOptions;
+import android.app.PendingIntent;
 import android.app.StatusBarManager;
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -33,6 +37,7 @@
 import android.hardware.SensorManager;
 import android.hardware.TriggerEvent;
 import android.hardware.TriggerEventListener;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.PowerManager;
 import android.os.PowerManager.WakeLock;
@@ -41,10 +46,12 @@
 import android.os.Trace;
 import android.os.UserHandle;
 import android.provider.Settings;
+import android.service.quickaccesswallet.QuickAccessWalletClient;
 import android.util.MutableBoolean;
 import android.util.Slog;
 import android.view.KeyEvent;
 
+import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.UiEvent;
@@ -70,7 +77,7 @@
      * Time in milliseconds in which the power button must be pressed twice so it will be considered
      * as a camera launch.
      */
-    @VisibleForTesting static final long CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS = 300;
+    @VisibleForTesting static final long POWER_DOUBLE_TAP_MAX_TIME_MS = 300;
 
 
     /**
@@ -100,10 +107,23 @@
     @VisibleForTesting
     static final int EMERGENCY_GESTURE_POWER_BUTTON_COOLDOWN_PERIOD_MS_MAX = 5000;
 
-    /**
-     * Number of taps required to launch camera shortcut.
-     */
-    private static final int CAMERA_POWER_TAP_COUNT_THRESHOLD = 2;
+    /** Indicates camera should be launched on power double tap. */
+    @VisibleForTesting static final int LAUNCH_CAMERA_ON_DOUBLE_TAP_POWER = 0;
+
+    /** Indicates wallet should be launched on power double tap. */
+    @VisibleForTesting static final int LAUNCH_WALLET_ON_DOUBLE_TAP_POWER = 1;
+
+    /** Number of taps required to launch the double tap shortcut (either camera or wallet). */
+    public static final int DOUBLE_POWER_TAP_COUNT_THRESHOLD = 2;
+
+    /** Bundle to send with PendingIntent to grant background activity start privileges. */
+    private static final Bundle GRANT_BACKGROUND_START_PRIVILEGES =
+            ActivityOptions.makeBasic()
+                    .setPendingIntentBackgroundActivityStartMode(
+                            ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS)
+                    .toBundle();
+
+    private final QuickAccessWalletClient mQuickAccessWalletClient;
 
     /** The listener that receives the gesture event. */
     private final GestureEventListener mGestureListener = new GestureEventListener();
@@ -158,6 +178,9 @@
      */
     private boolean mCameraDoubleTapPowerEnabled;
 
+    /** Whether wallet double tap power button gesture is currently enabled. */
+    private boolean mWalletDoubleTapPowerEnabled;
+
     /**
      * Whether emergency gesture is currently enabled
      */
@@ -204,15 +227,17 @@
         }
     }
     public GestureLauncherService(Context context) {
-        this(context, new MetricsLogger(), new UiEventLoggerImpl());
+        this(context, new MetricsLogger(),
+                QuickAccessWalletClient.create(context), new UiEventLoggerImpl());
     }
 
     @VisibleForTesting
     GestureLauncherService(Context context, MetricsLogger metricsLogger,
-            UiEventLogger uiEventLogger) {
+            QuickAccessWalletClient quickAccessWalletClient, UiEventLogger uiEventLogger) {
         super(context);
         mContext = context;
         mMetricsLogger = metricsLogger;
+        mQuickAccessWalletClient = quickAccessWalletClient;
         mUiEventLogger = uiEventLogger;
     }
 
@@ -237,6 +262,9 @@
                     "GestureLauncherService");
             updateCameraRegistered();
             updateCameraDoubleTapPowerEnabled();
+            if (launchWalletOptionOnPowerDoubleTap()) {
+                updateWalletDoubleTapPowerEnabled();
+            }
             updateEmergencyGestureEnabled();
             updateEmergencyGesturePowerButtonCooldownPeriodMs();
 
@@ -250,12 +278,24 @@
     }
 
     private void registerContentObservers() {
-        mContext.getContentResolver().registerContentObserver(
-                Settings.Secure.getUriFor(Settings.Secure.CAMERA_GESTURE_DISABLED),
-                false, mSettingObserver, mUserId);
-        mContext.getContentResolver().registerContentObserver(
-                Settings.Secure.getUriFor(Settings.Secure.CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED),
-                false, mSettingObserver, mUserId);
+        if (launchWalletOptionOnPowerDoubleTap()) {
+            mContext.getContentResolver().registerContentObserver(
+                    Settings.Secure.getUriFor(
+                            Settings.Secure.DOUBLE_TAP_POWER_BUTTON_GESTURE_ENABLED),
+                    false, mSettingObserver, mUserId);
+            mContext.getContentResolver().registerContentObserver(
+                    Settings.Secure.getUriFor(
+                            Settings.Secure.DOUBLE_TAP_POWER_BUTTON_GESTURE),
+                    false, mSettingObserver, mUserId);
+        } else {
+            mContext.getContentResolver().registerContentObserver(
+                    Settings.Secure.getUriFor(Settings.Secure.CAMERA_GESTURE_DISABLED),
+                    false, mSettingObserver, mUserId);
+            mContext.getContentResolver().registerContentObserver(
+                    Settings.Secure.getUriFor(
+                            Settings.Secure.CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED),
+                    false, mSettingObserver, mUserId);
+        }
         mContext.getContentResolver().registerContentObserver(
                 Settings.Secure.getUriFor(Settings.Secure.CAMERA_LIFT_TRIGGER_ENABLED),
                 false, mSettingObserver, mUserId);
@@ -292,6 +332,14 @@
     }
 
     @VisibleForTesting
+    void updateWalletDoubleTapPowerEnabled() {
+        boolean enabled = isWalletDoubleTapPowerSettingEnabled(mContext, mUserId);
+        synchronized (this) {
+            mWalletDoubleTapPowerEnabled = enabled;
+        }
+    }
+
+    @VisibleForTesting
     void updateEmergencyGestureEnabled() {
         boolean enabled = isEmergencyGestureSettingEnabled(mContext, mUserId);
         synchronized (this) {
@@ -418,10 +466,34 @@
                         Settings.Secure.CAMERA_GESTURE_DISABLED, 0, userId) == 0);
     }
 
+
+    /** Checks if camera should be launched on double press of the power button. */
     public static boolean isCameraDoubleTapPowerSettingEnabled(Context context, int userId) {
-        return isCameraDoubleTapPowerEnabled(context.getResources())
-                && (Settings.Secure.getIntForUser(context.getContentResolver(),
-                        Settings.Secure.CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED, 0, userId) == 0);
+        boolean res;
+
+        if (launchWalletOptionOnPowerDoubleTap()) {
+            res = isDoubleTapPowerGestureSettingEnabled(context, userId)
+                    && getDoubleTapPowerGestureAction(context, userId)
+                    == LAUNCH_CAMERA_ON_DOUBLE_TAP_POWER;
+        } else {
+            // These are legacy settings that will be deprecated once the option to launch both
+            // wallet and camera has been created.
+            res = isCameraDoubleTapPowerEnabled(context.getResources())
+                    && (Settings.Secure.getIntForUser(context.getContentResolver(),
+                    Settings.Secure.CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED, 0, userId) == 0);
+        }
+        return res;
+    }
+
+    /** Checks if wallet should be launched on double tap of the power button. */
+    public static boolean isWalletDoubleTapPowerSettingEnabled(Context context, int userId) {
+        if (!launchWalletOptionOnPowerDoubleTap()) {
+            return false;
+        }
+
+        return isDoubleTapPowerGestureSettingEnabled(context, userId)
+                && getDoubleTapPowerGestureAction(context, userId)
+                == LAUNCH_WALLET_ON_DOUBLE_TAP_POWER;
     }
 
     public static boolean isCameraLiftTriggerSettingEnabled(Context context, int userId) {
@@ -441,6 +513,28 @@
                 isDefaultEmergencyGestureEnabled(context.getResources()) ? 1 : 0, userId) != 0;
     }
 
+    private static int getDoubleTapPowerGestureAction(Context context, int userId) {
+        return Settings.Secure.getIntForUser(
+                context.getContentResolver(),
+                Settings.Secure.DOUBLE_TAP_POWER_BUTTON_GESTURE,
+                LAUNCH_CAMERA_ON_DOUBLE_TAP_POWER,
+                userId);
+    }
+
+    /** Whether the shortcut to launch app on power double press is enabled. */
+    private static boolean isDoubleTapPowerGestureSettingEnabled(Context context, int userId) {
+        return Settings.Secure.getIntForUser(
+                context.getContentResolver(),
+                Settings.Secure.DOUBLE_TAP_POWER_BUTTON_GESTURE_ENABLED,
+                isDoubleTapConfigEnabled(context.getResources()) ? 1 : 0,
+                userId)
+                == 1;
+    }
+
+    private static boolean isDoubleTapConfigEnabled(Resources resources) {
+        return resources.getBoolean(R.bool.config_doubleTapPowerGestureEnabled);
+    }
+
     /**
      * Gets power button cooldown period in milliseconds after emergency gesture is triggered. The
      * value is capped at a maximum
@@ -494,12 +588,19 @@
      * Whether GestureLauncherService should be enabled according to system properties.
      */
     public static boolean isGestureLauncherEnabled(Resources resources) {
-        return isCameraLaunchEnabled(resources)
-                || isCameraDoubleTapPowerEnabled(resources)
-                || isCameraLiftTriggerEnabled(resources)
-                || isEmergencyGestureEnabled(resources);
+        boolean res =
+                isCameraLaunchEnabled(resources)
+                        || isCameraLiftTriggerEnabled(resources)
+                        || isEmergencyGestureEnabled(resources);
+        if (launchWalletOptionOnPowerDoubleTap()) {
+            res |= isDoubleTapConfigEnabled(resources);
+        } else {
+            res |= isCameraDoubleTapPowerEnabled(resources);
+        }
+        return res;
     }
 
+
     /**
      * Attempts to intercept power key down event by detecting certain gesture patterns
      *
@@ -530,6 +631,7 @@
             return false;
         }
         boolean launchCamera = false;
+        boolean launchWallet = false;
         boolean launchEmergencyGesture = false;
         boolean intercept = false;
         long powerTapInterval;
@@ -541,7 +643,7 @@
                 mFirstPowerDown  = event.getEventTime();
                 mPowerButtonConsecutiveTaps = 1;
                 mPowerButtonSlowConsecutiveTaps = 1;
-            } else if (powerTapInterval >= CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS) {
+            } else if (powerTapInterval >= POWER_DOUBLE_TAP_MAX_TIME_MS) {
                 // Tap too slow for shortcuts
                 mFirstPowerDown  = event.getEventTime();
                 mPowerButtonConsecutiveTaps = 1;
@@ -586,10 +688,16 @@
                 }
             }
             if (mCameraDoubleTapPowerEnabled
-                    && powerTapInterval < CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS
-                    && mPowerButtonConsecutiveTaps == CAMERA_POWER_TAP_COUNT_THRESHOLD) {
+                    && powerTapInterval < POWER_DOUBLE_TAP_MAX_TIME_MS
+                    && mPowerButtonConsecutiveTaps == DOUBLE_POWER_TAP_COUNT_THRESHOLD) {
                 launchCamera = true;
                 intercept = interactive;
+            } else if (launchWalletOptionOnPowerDoubleTap()
+                    && mWalletDoubleTapPowerEnabled
+                    && powerTapInterval < POWER_DOUBLE_TAP_MAX_TIME_MS
+                    && mPowerButtonConsecutiveTaps == DOUBLE_POWER_TAP_COUNT_THRESHOLD) {
+                launchWallet = true;
+                intercept = interactive;
             }
         }
         if (mPowerButtonConsecutiveTaps > 1 || mPowerButtonSlowConsecutiveTaps > 1) {
@@ -608,6 +716,10 @@
                         (int) powerTapInterval);
                 mUiEventLogger.log(GestureLauncherEvent.GESTURE_CAMERA_DOUBLE_TAP_POWER);
             }
+        } else if (launchWallet) {
+            Slog.i(TAG, "Power button double tap gesture detected, launching wallet. Interval="
+                    + powerTapInterval + "ms");
+            launchWallet = sendGestureTargetActivityPendingIntent();
         } else if (launchEmergencyGesture) {
             Slog.i(TAG, "Emergency gesture detected, launching.");
             launchEmergencyGesture = handleEmergencyGesture();
@@ -623,11 +735,75 @@
                 mPowerButtonSlowConsecutiveTaps);
         mMetricsLogger.histogram("power_double_tap_interval", (int) powerTapInterval);
 
-        outLaunched.value = launchCamera || launchEmergencyGesture;
+        outLaunched.value = launchCamera || launchEmergencyGesture || launchWallet;
         // Intercept power key event if the press is part of a gesture (camera, eGesture) and the
         // user has completed setup.
         return intercept && isUserSetupComplete();
     }
+
+    /**
+     * Fetches and sends gestureTargetActivityPendingIntent from QuickAccessWallet, which is a
+     * specific activity that QuickAccessWalletService has defined to be launch on detection of the
+     * power button gesture.
+     */
+    private boolean sendGestureTargetActivityPendingIntent() {
+        boolean userSetupComplete = isUserSetupComplete();
+        if (mQuickAccessWalletClient == null
+                || !mQuickAccessWalletClient.isWalletServiceAvailable()) {
+            Slog.w(TAG, "QuickAccessWalletService is not available, ignoring wallet gesture.");
+            return false;
+        }
+
+        if (!userSetupComplete) {
+            if (DBG) {
+                Slog.d(TAG, "userSetupComplete = false, ignoring wallet gesture.");
+            }
+            return false;
+        }
+        if (DBG) {
+            Slog.d(TAG, "userSetupComplete = true, performing wallet gesture.");
+        }
+
+        mQuickAccessWalletClient.getGestureTargetActivityPendingIntent(
+                getContext().getMainExecutor(),
+                gesturePendingIntent -> {
+                    if (gesturePendingIntent == null) {
+                        Slog.d(TAG, "getGestureTargetActivityPendingIntent is null.");
+                        sendFallbackPendingIntent();
+                        return;
+                    }
+                    sendPendingIntentWithBackgroundStartPrivileges(gesturePendingIntent);
+                });
+        return true;
+    }
+
+    /**
+     * If gestureTargetActivityPendingIntent is null, this method is invoked to start the activity
+     * that QuickAccessWalletService has defined to host the Wallet view, which is typically the
+     * home screen of the Wallet application.
+     */
+    private void sendFallbackPendingIntent() {
+        mQuickAccessWalletClient.getWalletPendingIntent(
+                getContext().getMainExecutor(),
+                walletPendingIntent -> {
+                    if (walletPendingIntent == null) {
+                        Slog.w(TAG, "getWalletPendingIntent returns null. Not launching "
+                                + "anything for wallet.");
+                        return;
+                    }
+                    sendPendingIntentWithBackgroundStartPrivileges(walletPendingIntent);
+                });
+    }
+
+    private void sendPendingIntentWithBackgroundStartPrivileges(PendingIntent pendingIntent) {
+        try {
+            pendingIntent.send(GRANT_BACKGROUND_START_PRIVILEGES);
+        } catch (PendingIntent.CanceledException e) {
+            Slog.e(TAG, "PendingIntent was canceled", e);
+        }
+    }
+
+
     /**
      * @return true if camera was launched, false otherwise.
      */
@@ -709,6 +885,9 @@
                 registerContentObservers();
                 updateCameraRegistered();
                 updateCameraDoubleTapPowerEnabled();
+                if (launchWalletOptionOnPowerDoubleTap()) {
+                    updateWalletDoubleTapPowerEnabled();
+                }
                 updateEmergencyGestureEnabled();
                 updateEmergencyGesturePowerButtonCooldownPeriodMs();
             }
@@ -720,6 +899,9 @@
             if (userId == mUserId) {
                 updateCameraRegistered();
                 updateCameraDoubleTapPowerEnabled();
+                if (launchWalletOptionOnPowerDoubleTap()) {
+                    updateWalletDoubleTapPowerEnabled();
+                }
                 updateEmergencyGestureEnabled();
                 updateEmergencyGesturePowerButtonCooldownPeriodMs();
             }
diff --git a/services/core/java/com/android/server/TEST_MAPPING b/services/core/java/com/android/server/TEST_MAPPING
index 8da8358..96bdbb8 100644
--- a/services/core/java/com/android/server/TEST_MAPPING
+++ b/services/core/java/com/android/server/TEST_MAPPING
@@ -119,6 +119,10 @@
                     "include-filter": "android.os.storage.cts.StorageStatsManagerTest"
                 }
             ]
+        },
+        {
+            "name": "FrameworksMockingServicesTests_service_batteryServiceTest",
+            "file_patterns": ["BatteryService\\.java"]
         }
     ],
     "presubmit-large": [
@@ -176,10 +180,6 @@
                     "include-filter": "com.android.server.wm.BackgroundActivityStart*"
                 }
             ]
-        },
-        {
-            "name": "FrameworksMockingServicesTests_service_batteryServiceTest",
-            "file_patterns": ["BatteryService\\.java"]
         }
    ]
 }
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index c27126a..600aa1f 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -427,11 +427,14 @@
                 com.android.internal.R.bool.config_batteryStatsResetOnUnplugHighBatteryLevel);
         final boolean resetOnUnplugAfterSignificantCharge = context.getResources().getBoolean(
                 com.android.internal.R.bool.config_batteryStatsResetOnUnplugAfterSignificantCharge);
+        final int batteryHistoryStorageSize = context.getResources().getInteger(
+                com.android.internal.R.integer.config_batteryHistoryStorageSize);
         BatteryStatsImpl.BatteryStatsConfig.Builder batteryStatsConfigBuilder =
                 new BatteryStatsImpl.BatteryStatsConfig.Builder()
                         .setResetOnUnplugHighBatteryLevel(resetOnUnplugHighBatteryLevel)
                         .setResetOnUnplugAfterSignificantCharge(
-                                resetOnUnplugAfterSignificantCharge);
+                                resetOnUnplugAfterSignificantCharge)
+                        .setMaxHistorySizeBytes(batteryHistoryStorageSize);
         setPowerStatsThrottlePeriods(batteryStatsConfigBuilder, context.getResources().getString(
                 com.android.internal.R.string.config_powerStatsThrottlePeriods));
         mBatteryStatsConfig = batteryStatsConfigBuilder.build();
@@ -1101,6 +1104,9 @@
 
     /** StatsPullAtomCallback for pulling BatteryUsageStats data. */
     private class StatsPullAtomCallbackImpl implements StatsManager.StatsPullAtomCallback {
+        private static final long BATTERY_USAGE_STATS_PER_UID_MAX_STATS_AGE =
+                TimeUnit.HOURS.toMillis(2);
+
         @Override
         public int onPullAtom(int atomTag, List<StatsEvent> data) {
             final BatteryUsageStats bus;
@@ -1168,7 +1174,8 @@
                             .setMinConsumedPowerThreshold(minConsumedPowerThreshold);
 
                     if (isBatteryUsageStatsAccumulationSupported()) {
-                        query.accumulated();
+                        query.accumulated()
+                                .setMaxStatsAgeMs(BATTERY_USAGE_STATS_PER_UID_MAX_STATS_AGE);
                     }
 
                     bus = getBatteryUsageStats(List.of(query.build())).get(0);
diff --git a/services/core/java/com/android/server/am/BroadcastController.java b/services/core/java/com/android/server/am/BroadcastController.java
index 354f281..aa06b7e 100644
--- a/services/core/java/com/android/server/am/BroadcastController.java
+++ b/services/core/java/com/android/server/am/BroadcastController.java
@@ -316,8 +316,7 @@
                 return null;
             }
             if (callerApp.info.uid != SYSTEM_UID
-                    && !callerApp.getPkgList().containsKey(callerPackage)
-                    && !"android".equals(callerPackage)) {
+                    && !callerApp.getPkgList().containsKey(callerPackage)) {
                 throw new SecurityException("Given caller package " + callerPackage
                         + " is not running in process " + callerApp);
             }
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index f42641e..aadf6f6 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -2840,7 +2840,6 @@
                             return true;
                         }
                     }
-                    capability |= PROCESS_CAPABILITY_CPU_TIME;
                 }
                 // Not doing bind OOM management, so treat
                 // this guy more like a started service.
@@ -3089,7 +3088,6 @@
                         return true;
                     }
                 }
-                capability |= PROCESS_CAPABILITY_CPU_TIME;
             }
         }
         if (cr.hasFlag(Context.BIND_TREAT_LIKE_ACTIVITY)) {
@@ -4243,6 +4241,11 @@
                         != client.getSetCapability()) {
             // The connection might elevate the importance of the service's capabilities.
             needDryRun = true;
+        } else if (Flags.useCpuTimeCapability()
+                && (client.getSetCapability() & ~app.getSetCapability()
+                    & PROCESS_CAPABILITY_CPU_TIME) != 0) {
+            // The connection might grant PROCESS_CAPABILITY_CPU_TIME to the service.
+            needDryRun = true;
         } else if (Flags.unfreezeBindPolicyFix()
                 && cr.hasFlag(Context.BIND_WAIVE_PRIORITY
                             | Context.BIND_ALLOW_OOM_MANAGEMENT)) {
@@ -4290,6 +4293,10 @@
                 && client.mOptRecord.shouldNotFreeze()) {
             // Process has shouldNotFreeze and it could have gotten it from the client.
             return true;
+        } else if (Flags.useCpuTimeCapability()
+                && (client.getSetCapability() & app.getSetCapability()
+                    & PROCESS_CAPABILITY_CPU_TIME) != 0) {
+            return true;
         }
         return false;
     }
@@ -4309,6 +4316,11 @@
                 && client.mOptRecord.shouldNotFreeze()
                 && !app.mOptRecord.shouldNotFreeze()) {
             needDryRun = true;
+        } else if (Flags.useCpuTimeCapability()
+                && (client.getSetCapability() & ~app.getSetCapability()
+                    & PROCESS_CAPABILITY_CPU_TIME) != 0) {
+            // The connection might grant PROCESS_CAPABILITY_CPU_TIME to the provider.
+            needDryRun = true;
         }
 
         if (needDryRun) {
@@ -4335,6 +4347,10 @@
                 && client.mOptRecord.shouldNotFreeze()) {
             // Process has shouldNotFreeze and it could have gotten it from the client.
             return true;
+        } else if (Flags.useCpuTimeCapability()
+                && (client.getSetCapability() & app.getSetCapability()
+                    & PROCESS_CAPABILITY_CPU_TIME) != 0) {
+            return true;
         }
 
         return false;
diff --git a/services/core/java/com/android/server/app/GameManagerService.java b/services/core/java/com/android/server/app/GameManagerService.java
index 6f8dc10..c0a97db 100644
--- a/services/core/java/com/android/server/app/GameManagerService.java
+++ b/services/core/java/com/android/server/app/GameManagerService.java
@@ -1423,10 +1423,10 @@
             }
             final GameManagerSettings settings = mSettings.get(userId);
             // look for the existing GamePackageConfiguration override
-            configOverride = settings.getConfigOverride(packageName);
+            configOverride = settings.getConfigOverrideLocked(packageName);
             if (configOverride == null) {
                 configOverride = new GamePackageConfiguration(packageName);
-                settings.setConfigOverride(packageName, configOverride);
+                settings.setConfigOverrideLocked(packageName, configOverride);
             }
         }
         GamePackageConfiguration.GameModeConfiguration internalConfig =
@@ -1759,10 +1759,10 @@
             }
             final GameManagerSettings settings = mSettings.get(userId);
             // look for the existing GamePackageConfiguration override
-            configOverride = settings.getConfigOverride(packageName);
+            configOverride = settings.getConfigOverrideLocked(packageName);
             if (configOverride == null) {
                 configOverride = new GamePackageConfiguration(packageName);
-                settings.setConfigOverride(packageName, configOverride);
+                settings.setConfigOverrideLocked(packageName, configOverride);
             }
         }
         // modify GameModeConfiguration intervention settings
@@ -1801,7 +1801,7 @@
             }
             final GameManagerSettings settings = mSettings.get(userId);
             if (gameModeToReset != -1) {
-                final GamePackageConfiguration configOverride = settings.getConfigOverride(
+                final GamePackageConfiguration configOverride = settings.getConfigOverrideLocked(
                         packageName);
                 if (configOverride == null) {
                     return;
@@ -1812,10 +1812,10 @@
                 }
                 configOverride.removeModeConfig(gameModeToReset);
                 if (!configOverride.hasActiveGameModeConfig()) {
-                    settings.removeConfigOverride(packageName);
+                    settings.removeConfigOverrideLocked(packageName);
                 }
             } else {
-                settings.removeConfigOverride(packageName);
+                settings.removeConfigOverrideLocked(packageName);
             }
         }
 
@@ -2030,7 +2030,7 @@
 
         synchronized (mLock) {
             if (mSettings.containsKey(userId)) {
-                overrideConfig = mSettings.get(userId).getConfigOverride(packageName);
+                overrideConfig = mSettings.get(userId).getConfigOverrideLocked(packageName);
             }
         }
         if (overrideConfig == null || config == null) {
@@ -2075,7 +2075,7 @@
                                 }
                                 synchronized (mLock) {
                                     if (mSettings.containsKey(userId)) {
-                                        mSettings.get(userId).removeGame(packageName);
+                                        mSettings.get(userId).removeGameLocked(packageName);
                                     }
                                     sendUserMessage(userId, WRITE_SETTINGS,
                                             Intent.ACTION_PACKAGE_REMOVED, WRITE_DELAY_MILLIS);
diff --git a/services/core/java/com/android/server/app/GameManagerSettings.java b/services/core/java/com/android/server/app/GameManagerSettings.java
index b084cf3..c57a1f7 100644
--- a/services/core/java/com/android/server/app/GameManagerSettings.java
+++ b/services/core/java/com/android/server/app/GameManagerSettings.java
@@ -116,7 +116,7 @@
      * Removes all game settings of a given package.
      * This operation must be synced with an external lock.
      */
-    void removeGame(String packageName) {
+    void removeGameLocked(String packageName) {
         mGameModes.remove(packageName);
         mConfigOverrides.remove(packageName);
     }
@@ -125,7 +125,7 @@
      * Returns the game config override of a given package or null if absent.
      * This operation must be synced with an external lock.
      */
-    GamePackageConfiguration getConfigOverride(String packageName) {
+    GamePackageConfiguration getConfigOverrideLocked(String packageName) {
         return mConfigOverrides.get(packageName);
     }
 
@@ -133,7 +133,7 @@
      * Sets the game config override of a given package.
      * This operation must be synced with an external lock.
      */
-    void setConfigOverride(String packageName, GamePackageConfiguration configOverride) {
+    void setConfigOverrideLocked(String packageName, GamePackageConfiguration configOverride) {
         mConfigOverrides.put(packageName, configOverride);
     }
 
@@ -141,7 +141,7 @@
      * Removes the game mode config override of a given package.
      * This operation must be synced with an external lock.
      */
-    void removeConfigOverride(String packageName) {
+    void removeConfigOverrideLocked(String packageName) {
         mConfigOverrides.remove(packageName);
     }
 
diff --git a/services/core/java/com/android/server/appop/AppOpsUidStateTrackerImpl.java b/services/core/java/com/android/server/appop/AppOpsUidStateTrackerImpl.java
index fa2e674..ca9a25b 100644
--- a/services/core/java/com/android/server/appop/AppOpsUidStateTrackerImpl.java
+++ b/services/core/java/com/android/server/appop/AppOpsUidStateTrackerImpl.java
@@ -28,6 +28,7 @@
 import static android.app.AppOpsManager.MODE_FOREGROUND;
 import static android.app.AppOpsManager.MODE_IGNORED;
 import static android.app.AppOpsManager.OP_CAMERA;
+import static android.app.AppOpsManager.OP_CONTROL_AUDIO;
 import static android.app.AppOpsManager.OP_NONE;
 import static android.app.AppOpsManager.OP_RECEIVE_EXPLICIT_USER_INTERACTION_AUDIO;
 import static android.app.AppOpsManager.OP_RECORD_AUDIO;
@@ -176,6 +177,8 @@
             case OP_RECORD_AUDIO:
             case OP_RECEIVE_EXPLICIT_USER_INTERACTION_AUDIO:
                 return PROCESS_CAPABILITY_FOREGROUND_MICROPHONE;
+            case OP_CONTROL_AUDIO:
+                return PROCESS_CAPABILITY_FOREGROUND_AUDIO_CONTROL;
             default:
                 return PROCESS_CAPABILITY_NONE;
         }
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 1799b77..0fd4716 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -11845,7 +11845,7 @@
 
     private AudioDeviceAttributes anonymizeAudioDeviceAttributesUnchecked(
             AudioDeviceAttributes ada) {
-        if (!AudioSystem.isBluetoothDevice(ada.getInternalType())) {
+        if (ada == null || !AudioSystem.isBluetoothDevice(ada.getInternalType())) {
             return ada;
         }
         AudioDeviceAttributes res = new AudioDeviceAttributes(ada);
diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java
index 6feae34..8b6b0f9 100644
--- a/services/core/java/com/android/server/compat/PlatformCompat.java
+++ b/services/core/java/com/android/server/compat/PlatformCompat.java
@@ -523,7 +523,7 @@
         // older target sdk to impact all system uid apps
         if (Flags.systemUidTargetSystemSdk() && !mIsWear &&
                 uid == Process.SYSTEM_UID && appInfo != null) {
-            appInfo.targetSdkVersion = Build.VERSION.SDK_INT;
+            appInfo.targetSdkVersion = mBuildClassifier.platformTargetSdk();
         }
         return appInfo;
     }
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 4c5f652..ac0892b 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -1960,6 +1960,10 @@
     public void onUserAdded(int userId) {
         // If the user is restricted tie them to the parent user's VPN
         UserInfo user = mUserManager.getUserInfo(userId);
+        if (user == null) {
+            Log.e(TAG, "Can not retrieve UserInfo for userId=" + userId);
+            return;
+        }
         if (user.isRestricted() && user.restrictedProfileParentId == mUserId) {
             synchronized(Vpn.this) {
                 final Set<Range<Integer>> existingRanges = mNetworkCapabilities.getUids();
@@ -1989,6 +1993,14 @@
     public void onUserRemoved(int userId) {
         // clean up if restricted
         UserInfo user = mUserManager.getUserInfo(userId);
+        // TODO: Retrieving UserInfo upon receiving the USER_REMOVED intent is not guaranteed.
+        //  This could prevent the removal of associated ranges. To ensure proper range removal,
+        //  store the user info when adding ranges. This allows using the user ID in the
+        //  USER_REMOVED intent to handle the removal process.
+        if (user == null) {
+            Log.e(TAG, "Can not retrieve UserInfo for userId=" + userId);
+            return;
+        }
         if (user.isRestricted() && user.restrictedProfileParentId == mUserId) {
             synchronized(Vpn.this) {
                 final Set<Range<Integer>> existingRanges = mNetworkCapabilities.getUids();
diff --git a/services/core/java/com/android/server/display/BrightnessRangeController.java b/services/core/java/com/android/server/display/BrightnessRangeController.java
index 83b0801..50d6508 100644
--- a/services/core/java/com/android/server/display/BrightnessRangeController.java
+++ b/services/core/java/com/android/server/display/BrightnessRangeController.java
@@ -37,7 +37,6 @@
     private final HdrClamper mHdrClamper;
 
     private final Runnable mModeChangeCallback;
-    private final boolean mUseNbmController;
 
     private final boolean mUseHdrClamper;
 
@@ -62,11 +61,8 @@
         mHdrClamper = hdrClamper;
         mNormalBrightnessModeController = normalBrightnessModeController;
         mUseHdrClamper = flags.isHdrClamperEnabled() && !flags.useNewHdrBrightnessModifier();
-        mUseNbmController = flags.isNbmControllerEnabled();
-        if (mUseNbmController) {
-            mNormalBrightnessModeController.resetNbmData(
-                    displayDeviceConfig.getLuxThrottlingData());
-        }
+        mNormalBrightnessModeController.resetNbmData(
+                displayDeviceConfig.getLuxThrottlingData());
         if (flags.useNewHdrBrightnessModifier()) {
             // HDR boost is handled by HdrBrightnessModifier and should be disabled in HbmController
             mHbmController.disableHdrBoost();
@@ -76,7 +72,6 @@
 
     void dump(PrintWriter pw) {
         pw.println("BrightnessRangeController:");
-        pw.println("  mUseNormalBrightnessController=" + mUseNbmController);
         pw.println("  mUseHdrClamper=" + mUseHdrClamper);
         mHbmController.dump(pw);
         mNormalBrightnessModeController.dump(pw);
@@ -138,9 +133,7 @@
     float getCurrentBrightnessMax() {
         // nbmController might adjust maxBrightness only if device does not support HBM or
         // hbm is currently not allowed
-        if (mUseNbmController
-                && (!mHbmController.deviceSupportsHbm()
-                || !mHbmController.isHbmCurrentlyAllowed())) {
+        if (!mHbmController.deviceSupportsHbm() || !mHbmController.isHbmCurrentlyAllowed()) {
             return Math.min(mHbmController.getCurrentBrightnessMax(),
                     mNormalBrightnessModeController.getCurrentBrightnessMax());
         }
@@ -173,16 +166,12 @@
     }
 
     private void applyChanges(BooleanSupplier nbmChangesFunc, Runnable hbmChangesFunc) {
-        if (mUseNbmController) {
-            boolean nbmTransitionChanged = nbmChangesFunc.getAsBoolean();
-            hbmChangesFunc.run();
-            // if nbm transition changed - trigger callback
-            // HighBrightnessModeController handles sending changes itself
-            if (nbmTransitionChanged) {
-                mModeChangeCallback.run();
-            }
-        } else {
-            hbmChangesFunc.run();
+        boolean nbmTransitionChanged = nbmChangesFunc.getAsBoolean();
+        hbmChangesFunc.run();
+        // if nbm transition changed - trigger callback
+        // HighBrightnessModeController handles sending changes itself
+        if (nbmTransitionChanged) {
+            mModeChangeCallback.run();
         }
     }
 
diff --git a/services/core/java/com/android/server/display/DisplayDevice.java b/services/core/java/com/android/server/display/DisplayDevice.java
index 4bbddae..2bdb5c2 100644
--- a/services/core/java/com/android/server/display/DisplayDevice.java
+++ b/services/core/java/com/android/server/display/DisplayDevice.java
@@ -306,6 +306,14 @@
     }
 
     /**
+     * Returns if the display should only mirror another display rather than showing other content
+     * until it is destroyed.
+     */
+    public boolean shouldOnlyMirror() {
+        return false;
+    }
+
+    /**
      * Sets the display layer stack while in a transaction.
      */
     public final void setLayerStackLocked(SurfaceControl.Transaction t, int layerStack,
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 0b633bd..bad5b8b 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -199,7 +199,6 @@
 import java.util.concurrent.atomic.AtomicLong;
 import java.util.function.Consumer;
 
-
 /**
  * Manages attached displays.
  * <p>
@@ -906,6 +905,16 @@
         }
     }
 
+    @VisibleForTesting
+    ContentObserver getSettingsObserver() {
+        return mSettingsObserver;
+    }
+
+    @VisibleForTesting
+    boolean shouldMirrorBuiltInDisplay() {
+        return mMirrorBuiltInDisplay;
+    }
+
     DisplayNotificationManager getDisplayNotificationManager() {
         return mDisplayNotificationManager;
     }
@@ -1230,11 +1239,6 @@
     }
 
     private void updateMirrorBuiltInDisplaySettingLocked() {
-        if (!mFlags.isDisplayContentModeManagementEnabled()) {
-            Slog.e(TAG, "MirrorBuiltInDisplay setting shouldn't be updated when the flag is off.");
-            return;
-        }
-
         synchronized (mSyncRoot) {
             ContentResolver resolver = mContext.getContentResolver();
             final boolean mirrorBuiltInDisplay = Settings.Secure.getIntForUser(resolver,
@@ -1243,6 +1247,9 @@
                 return;
             }
             mMirrorBuiltInDisplay = mirrorBuiltInDisplay;
+            if (mFlags.isDisplayContentModeManagementEnabled()) {
+                mLogicalDisplayMapper.forEachLocked(this::updateCanHostTasksIfNeededLocked);
+            }
         }
     }
 
@@ -2308,6 +2315,10 @@
         mDisplayBrightnesses.append(displayId,
                 new BrightnessPair(brightnessDefault, brightnessDefault));
 
+        if (mFlags.isDisplayContentModeManagementEnabled()) {
+            updateCanHostTasksIfNeededLocked(display);
+        }
+
         DisplayManagerGlobal.invalidateLocalDisplayInfoCaches();
     }
 
@@ -2630,6 +2641,12 @@
         }
     }
 
+    private void updateCanHostTasksIfNeededLocked(LogicalDisplay display) {
+        if (display.setCanHostTasksLocked(!mMirrorBuiltInDisplay)) {
+            sendDisplayEventIfEnabledLocked(display, DisplayManagerGlobal.EVENT_DISPLAY_CHANGED);
+        }
+    }
+
     private void recordTopInsetLocked(@Nullable LogicalDisplay d) {
         // We must only persist the inset after boot has completed, otherwise we will end up
         // overwriting the persisted value before the masking flag has been loaded from the
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index f48fbea..2f82b2a 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -521,6 +521,8 @@
             BrightnessTracker brightnessTracker, BrightnessSetting brightnessSetting,
             Runnable onBrightnessChangeRunnable, HighBrightnessModeMetadata hbmMetadata,
             boolean bootCompleted, DisplayManagerFlags flags) {
+        final Resources resources = context.getResources();
+
         mFlags = flags;
         mInjector = injector != null ? injector : new Injector();
         mClock = mInjector.getClock();
@@ -540,7 +542,9 @@
         mDisplayPowerProximityStateController = mInjector.getDisplayPowerProximityStateController(
                 mWakelockController, mDisplayDeviceConfig, mHandler.getLooper(),
                 () -> updatePowerState(), mDisplayId, mSensorManager);
-        mDisplayStateController = new DisplayStateController(mDisplayPowerProximityStateController);
+        mDisplayStateController = new DisplayStateController(
+            mDisplayPowerProximityStateController,
+            resources.getBoolean(R.bool.config_skipScreenOffTransition));
         mTag = TAG + "[" + mDisplayId + "]";
         mThermalBrightnessThrottlingDataId =
                 logicalDisplay.getDisplayInfoLocked().thermalBrightnessThrottlingDataId;
@@ -574,17 +578,14 @@
                 Settings.Global.getUriFor(Settings.Global.Wearable.BEDTIME_MODE),
                 false /*notifyForDescendants*/, mSettingsObserver, UserHandle.USER_ALL);
 
-        final Resources resources = context.getResources();
-
         // DOZE AND DIM SETTINGS
         mScreenBrightnessDozeConfig = BrightnessUtils.clampAbsoluteBrightness(
                 mDisplayDeviceConfig.getDefaultDozeBrightness());
         loadBrightnessRampRates();
         mSkipScreenOnBrightnessRamp = resources.getBoolean(
                 R.bool.config_skipScreenOnBrightnessRamp);
-        mDozeScaleFactor = context.getResources().getFraction(
-                R.fraction.config_screenAutoBrightnessDozeScaleFactor,
-                1, 1);
+        mDozeScaleFactor = resources.getFraction(
+                R.fraction.config_screenAutoBrightnessDozeScaleFactor, 1, 1);
 
         Runnable modeChangeCallback = () -> {
             sendUpdatePowerState();
@@ -1646,6 +1647,7 @@
                 (mBrightnessReasonTemp.getReason() == BrightnessReason.REASON_TEMPORARY)
                         || mAutomaticBrightnessStrategy
                         .isTemporaryAutoBrightnessAdjustmentApplied();
+        float rampSpeed = 0;
         if (!mPendingScreenOff) {
             if (mSkipScreenOnBrightnessRamp) {
                 if (state == Display.STATE_ON) {
@@ -1747,7 +1749,6 @@
                             customAnimationRate, /* ignoreAnimationLimits = */true);
                 } else {
                     boolean isIncreasing = animateValue > currentBrightness;
-                    final float rampSpeed;
                     final boolean idle = mAutomaticBrightnessController != null
                             && mAutomaticBrightnessController.isInIdleMode();
                     if (isIncreasing && slowChange) {
@@ -1832,6 +1833,8 @@
                 .getDisplayBrightnessStrategyName());
         mTempBrightnessEvent.setAutomaticBrightnessEnabled(
                 displayBrightnessState.getShouldUseAutoBrightness());
+        mTempBrightnessEvent.setSlowChange(slowChange);
+        mTempBrightnessEvent.setRampSpeed(rampSpeed);
         // Temporary is what we use during slider interactions. We avoid logging those so that
         // we don't spam logcat when the slider is being used.
         boolean tempToTempTransition =
@@ -2233,7 +2236,6 @@
                     setReportedScreenState(REPORTED_TO_POLICY_SCREEN_TURNING_OFF);
                     blockScreenOff();
                     mWindowManagerPolicy.screenTurningOff(mDisplayId, mPendingScreenOffUnblocker);
-                    unblockScreenOff();
                 } else if (mPendingScreenOffUnblocker != null) {
                     // Abort doing the state change until screen off is unblocked.
                     return false;
diff --git a/services/core/java/com/android/server/display/ExternalDisplayPolicy.java b/services/core/java/com/android/server/display/ExternalDisplayPolicy.java
index f34d2cc..519763a 100644
--- a/services/core/java/com/android/server/display/ExternalDisplayPolicy.java
+++ b/services/core/java/com/android/server/display/ExternalDisplayPolicy.java
@@ -217,8 +217,10 @@
 
         mExternalDisplayStatsService.onDisplayConnected(logicalDisplay);
 
-        if ((Build.IS_ENG || Build.IS_USERDEBUG)
-                && SystemProperties.getBoolean(ENABLE_ON_CONNECT, false)) {
+        if (((Build.IS_ENG || Build.IS_USERDEBUG)
+                        && SystemProperties.getBoolean(ENABLE_ON_CONNECT, false))
+                || (mFlags.isDisplayContentModeManagementEnabled()
+                        && logicalDisplay.canHostTasksLocked())) {
             Slog.w(TAG, "External display is enabled by default, bypassing user consent.");
             mInjector.sendExternalDisplayEventLocked(logicalDisplay, EVENT_DISPLAY_CONNECTED);
             return;
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index 1de9c95..f9d4137 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -17,6 +17,7 @@
 package com.android.server.display;
 
 import static com.android.server.display.DisplayDeviceInfo.TOUCH_NONE;
+import static com.android.server.display.layout.Layout.Display.POSITION_REAR;
 import static com.android.server.wm.utils.DisplayInfoOverrides.WM_OVERRIDE_FIELDS;
 import static com.android.server.wm.utils.DisplayInfoOverrides.copyDisplayInfoFields;
 
@@ -227,6 +228,8 @@
      */
     private final boolean mIsAnisotropyCorrectionEnabled;
 
+    private boolean mCanHostTasks;
+
     LogicalDisplay(int displayId, int layerStack, DisplayDevice primaryDisplayDevice) {
         this(displayId, layerStack, primaryDisplayDevice, false, false);
     }
@@ -245,6 +248,7 @@
         mBaseDisplayInfo.thermalBrightnessThrottlingDataId = mThermalBrightnessThrottlingDataId;
         mIsAnisotropyCorrectionEnabled = isAnisotropyCorrectionEnabled;
         mAlwaysRotateDisplayDeviceEnabled = isAlwaysRotateDisplayDeviceEnabled;
+        mCanHostTasks = (mDisplayId == Display.DEFAULT_DISPLAY);
     }
 
     public void setDevicePositionLocked(int position) {
@@ -568,6 +572,7 @@
             mBaseDisplayInfo.layoutLimitedRefreshRate = mLayoutLimitedRefreshRate;
             mBaseDisplayInfo.thermalRefreshRateThrottling = mThermalRefreshRateThrottling;
             mBaseDisplayInfo.thermalBrightnessThrottlingDataId = mThermalBrightnessThrottlingDataId;
+            mBaseDisplayInfo.canHostTasks = mCanHostTasks;
 
             mPrimaryDisplayDeviceInfo = deviceInfo;
             mInfo.set(null);
@@ -927,6 +932,61 @@
         return handleLogicalDisplayChangedLocked;
     }
 
+    boolean canHostTasksLocked() {
+        return mCanHostTasks;
+    }
+
+    /**
+     * Sets whether the display can host tasks.
+     *
+     * @param canHostTasks Whether the display can host tasks according to the user's setting.
+     * @return Whether Display Manager should call sendDisplayEventIfEnabledLocked().
+     */
+    boolean setCanHostTasksLocked(boolean canHostTasks) {
+        canHostTasks = validateCanHostTasksLocked(canHostTasks);
+        if (mBaseDisplayInfo.canHostTasks == canHostTasks) {
+            return false;
+        }
+
+        mCanHostTasks = canHostTasks;
+        mBaseDisplayInfo.canHostTasks = canHostTasks;
+        mInfo.set(null);
+        return true;
+    }
+
+    /**
+     * Checks whether the display's ability to host tasks should be determined independently of the
+     * user's setting value. If so, returns the actual validated value based on the display's
+     * usage; otherwise, returns the user's setting value.
+     *
+     * @param canHostTasks Whether the display can host tasks according to the user's setting.
+     * @return Whether the display can actually host task after configuration.
+     */
+    private boolean validateCanHostTasksLocked(boolean canHostTasks) {
+        // The default display can always host tasks.
+        if (getDisplayIdLocked() == Display.DEFAULT_DISPLAY) {
+            return true;
+        }
+
+        // The display that should only mirror can never host tasks.
+        if (mPrimaryDisplayDevice.shouldOnlyMirror()) {
+            return false;
+        }
+
+        // The display that has its own content can always host tasks.
+        final boolean isRearDisplay = getDevicePositionLocked() == POSITION_REAR;
+        final boolean ownContent =
+                ((mPrimaryDisplayDevice.getDisplayDeviceInfoLocked().flags
+                        & DisplayDeviceInfo.FLAG_OWN_CONTENT_ONLY)
+                        != 0)
+                        || isRearDisplay;
+        if (ownContent) {
+            return true;
+        }
+
+        return canHostTasks;
+    }
+
     /**
      * Swap the underlying {@link DisplayDevice} with the specified LogicalDisplay.
      *
diff --git a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
index f14e452..558afd1 100644
--- a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
@@ -307,13 +307,14 @@
 
     private VirtualDisplayDevice removeVirtualDisplayDeviceLocked(IBinder appToken) {
         if (getFeatureFlags().isVirtualDisplayLimitEnabled()) {
-            int ownerUid = mOwnerUids.get(appToken);
-            int noOfDevices = mNoOfDevicesPerPackage.get(ownerUid, /* valueIfKeyNotFound= */ 0);
-            if (noOfDevices <= 1) {
-                mNoOfDevicesPerPackage.delete(ownerUid);
-                mOwnerUids.remove(appToken);
-            } else {
-                mNoOfDevicesPerPackage.put(ownerUid, noOfDevices - 1);
+            Integer ownerUid = mOwnerUids.remove(appToken);
+            if (ownerUid != null) {
+                int noOfDevices = mNoOfDevicesPerPackage.get(ownerUid, /* valueIfKeyNotFound= */ 0);
+                if (noOfDevices <= 1) {
+                    mNoOfDevicesPerPackage.delete(ownerUid);
+                } else {
+                    mNoOfDevicesPerPackage.put(ownerUid, noOfDevices - 1);
+                }
             }
         }
         return mVirtualDisplayDevices.remove(appToken);
@@ -500,6 +501,11 @@
             mPendingChanges = 0;
         }
 
+        @Override
+        public boolean shouldOnlyMirror() {
+            return mProjection != null || ((mFlags & VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR) != 0);
+        }
+
         public void setSurfaceLocked(Surface surface) {
             if (!mStopped && mSurface != surface) {
                 if (mDisplayState == Display.STATE_ON
diff --git a/services/core/java/com/android/server/display/brightness/BrightnessEvent.java b/services/core/java/com/android/server/display/brightness/BrightnessEvent.java
index 9e9b899..159c30d 100644
--- a/services/core/java/com/android/server/display/brightness/BrightnessEvent.java
+++ b/services/core/java/com/android/server/display/brightness/BrightnessEvent.java
@@ -78,6 +78,8 @@
     private String mDisplayBrightnessStrategyName;
     @AutomaticBrightnessController.AutomaticBrightnessMode
     private int mAutoBrightnessMode;
+    private boolean mSlowChange;
+    private float mRampSpeed;
 
     public BrightnessEvent(BrightnessEvent that) {
         copyFrom(that);
@@ -126,6 +128,8 @@
         mAutomaticBrightnessEnabled = that.isAutomaticBrightnessEnabled();
         mDisplayBrightnessStrategyName = that.getDisplayBrightnessStrategyName();
         mAutoBrightnessMode = that.mAutoBrightnessMode;
+        mSlowChange = that.mSlowChange;
+        mRampSpeed = that.mRampSpeed;
     }
 
     /**
@@ -163,6 +167,8 @@
         mAutomaticBrightnessEnabled = true;
         mDisplayBrightnessStrategyName = "";
         mAutoBrightnessMode = AUTO_BRIGHTNESS_MODE_DEFAULT;
+        mSlowChange = false;
+        mRampSpeed = 0;
     }
 
     /**
@@ -248,7 +254,9 @@
                 + ", powerFactor=" + mPowerFactor
                 // Meta
                 + ", physDisp=" + mPhysicalDisplayName + "(" + mPhysicalDisplayId + ")"
-                + ", logicalId=" + mDisplayId;
+                + ", logicalId=" + mDisplayId
+                + ", slowChange=" + mSlowChange
+                + ", rampSpeed=" + mRampSpeed;
     }
 
     @Override
@@ -469,8 +477,8 @@
         return mDisplayBrightnessStrategyName;
     }
 
-    public void setAutomaticBrightnessEnabled(boolean mAutomaticBrightnessEnabled) {
-        this.mAutomaticBrightnessEnabled = mAutomaticBrightnessEnabled;
+    public void setAutomaticBrightnessEnabled(boolean automaticBrightnessEnabled) {
+        mAutomaticBrightnessEnabled = automaticBrightnessEnabled;
     }
 
     @AutomaticBrightnessController.AutomaticBrightnessMode
@@ -483,6 +491,14 @@
         mAutoBrightnessMode = mode;
     }
 
+    public void setSlowChange(boolean slowChange) {
+        mSlowChange = slowChange;
+    }
+
+    public void setRampSpeed(float rampSpeed) {
+        mRampSpeed = rampSpeed;
+    }
+
     /**
      * A utility to stringify flags from a BrightnessEvent
      * @return Stringified flags from BrightnessEvent
diff --git a/services/core/java/com/android/server/display/color/ColorDisplayService.java b/services/core/java/com/android/server/display/color/ColorDisplayService.java
index 7892639..52e6490 100644
--- a/services/core/java/com/android/server/display/color/ColorDisplayService.java
+++ b/services/core/java/com/android/server/display/color/ColorDisplayService.java
@@ -168,8 +168,7 @@
             new NightDisplayTintController();
     private final TintController mGlobalSaturationTintController =
             new GlobalSaturationTintController();
-    private final ReduceBrightColorsTintController mReduceBrightColorsTintController =
-            new ReduceBrightColorsTintController();
+    private final ReduceBrightColorsTintController mReduceBrightColorsTintController;
 
     @VisibleForTesting
     final Handler mHandler;
@@ -201,7 +200,13 @@
     private boolean mEvenDimmerActivated;
 
     public ColorDisplayService(Context context) {
+        this(context, new ReduceBrightColorsTintController());
+    }
+
+    @VisibleForTesting
+    public ColorDisplayService(Context context, ReduceBrightColorsTintController rbcController) {
         super(context);
+        mReduceBrightColorsTintController = rbcController;
         mHandler = new TintHandler(DisplayThread.get().getLooper());
         mVisibleBackgroundUsersEnabled = isVisibleBackgroundUsersEnabled();
         mUserManager = UserManagerService.getInstance();
@@ -571,27 +576,37 @@
         return mColorModeCompositionColorSpaces.get(mode, Display.COLOR_MODE_INVALID);
     }
 
-    private void onDisplayColorModeChanged(int mode) {
+    @VisibleForTesting
+    void onDisplayColorModeChanged(int mode) {
         if (mode == NOT_SET) {
             return;
         }
 
+        mReduceBrightColorsTintController.cancelAnimator();
         mNightDisplayTintController.cancelAnimator();
         mDisplayWhiteBalanceTintController.cancelAnimator();
 
+        final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
+
         if (mNightDisplayTintController.isAvailable(getContext())) {
-            final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
             mNightDisplayTintController.setUp(getContext(), dtm.needsLinearColorMatrix(mode));
             mNightDisplayTintController
                     .setMatrix(mNightDisplayTintController.getColorTemperatureSetting());
         }
 
+        if (mReduceBrightColorsTintController.isAvailable(getContext())) {
+            // Different color modes may require different coefficients to be loaded for RBC.
+            // Re-set up RBC so that it can recalculate its transform matrix with new values.
+            mReduceBrightColorsTintController.setUp(getContext(), dtm.needsLinearColorMatrix(mode));
+            onReduceBrightColorsStrengthLevelChanged(); // Trigger matrix recalc + updates
+        }
+
         // dtm.setColorMode() needs to be called before
         // updateDisplayWhiteBalanceStatus(), this is because the latter calls
         // DisplayTransformManager.needsLinearColorMatrix(), therefore it is dependent
         // on the state of DisplayTransformManager.
-        final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
         dtm.setColorMode(mode, mNightDisplayTintController.getMatrix(),
+                mReduceBrightColorsTintController.getMatrix(),
                 getCompositionColorSpace(mode));
 
         if (mDisplayWhiteBalanceTintController.isAvailable(getContext())) {
diff --git a/services/core/java/com/android/server/display/color/DisplayTransformManager.java b/services/core/java/com/android/server/display/color/DisplayTransformManager.java
index a76c427..cb7b177 100644
--- a/services/core/java/com/android/server/display/color/DisplayTransformManager.java
+++ b/services/core/java/com/android/server/display/color/DisplayTransformManager.java
@@ -265,7 +265,7 @@
     /**
      * Sets color mode and updates night display transform values.
      */
-    public boolean setColorMode(int colorMode, float[] nightDisplayMatrix,
+    public boolean setColorMode(int colorMode, float[] nightDisplayMatrix, float[] rbcMatrix,
             int compositionColorMode) {
         if (colorMode == ColorDisplayManager.COLOR_MODE_NATURAL) {
             applySaturation(COLOR_SATURATION_NATURAL);
@@ -285,7 +285,11 @@
             setDisplayColor(colorMode, compositionColorMode);
         }
 
+        // These are close to the setDisplayColor() call to reduce delay between
+        // setting these matrixes and updating the color mode. Without this proximity
+        // of calls, updates to color mode can result in flicker.
         setColorMatrix(LEVEL_COLOR_MATRIX_NIGHT_DISPLAY, nightDisplayMatrix);
+        setColorMatrix(LEVEL_COLOR_MATRIX_REDUCE_BRIGHT_COLORS, rbcMatrix);
 
         updateConfiguration();
 
diff --git a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
index 78bd41b..85b6bbb 100644
--- a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
+++ b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
@@ -46,10 +46,6 @@
             Flags.FLAG_ENABLE_CONNECTED_DISPLAY_MANAGEMENT,
             Flags::enableConnectedDisplayManagement);
 
-    private final FlagState mNbmControllerFlagState = new FlagState(
-            Flags.FLAG_ENABLE_NBM_CONTROLLER,
-            Flags::enableNbmController);
-
     private final FlagState mHdrClamperFlagState = new FlagState(
             Flags.FLAG_ENABLE_HDR_CLAMPER,
             Flags::enableHdrClamper);
@@ -282,11 +278,6 @@
         return mConnectedDisplayManagementFlagState.isEnabled();
     }
 
-    /** Returns whether NBM Controller is enabled or not. */
-    public boolean isNbmControllerEnabled() {
-        return mNbmControllerFlagState.isEnabled();
-    }
-
     /** Returns whether hdr clamper is enabled on not. */
     public boolean isHdrClamperEnabled() {
         return mHdrClamperFlagState.isEnabled();
@@ -595,7 +586,6 @@
         pw.println(" " + mExternalDisplayLimitModeState);
         pw.println(" " + mDisplayTopology);
         pw.println(" " + mHdrClamperFlagState);
-        pw.println(" " + mNbmControllerFlagState);
         pw.println(" " + mPowerThrottlingClamperFlagState);
         pw.println(" " + mEvenDimmerFlagState);
         pw.println(" " + mSmallAreaDetectionFlagState);
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 123b7df..3358f72 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
@@ -37,14 +37,6 @@
 }
 
 flag {
-    name: "enable_nbm_controller"
-    namespace: "display_manager"
-    description: "Feature flag for Normal Brightness Mode Controller"
-    bug: "299527549"
-    is_fixed_read_only: true
-}
-
-flag {
     name: "enable_hdr_clamper"
     namespace: "display_manager"
     description: "Feature flag for HDR Clamper"
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 0b46e0f..f3b0799 100644
--- a/services/core/java/com/android/server/display/state/DisplayStateController.java
+++ b/services/core/java/com/android/server/display/state/DisplayStateController.java
@@ -31,14 +31,17 @@
  * clients about the changes
  */
 public class DisplayStateController {
-    private DisplayPowerProximityStateController mDisplayPowerProximityStateController;
+    private final DisplayPowerProximityStateController mDisplayPowerProximityStateController;
+    private final boolean mShouldSkipScreenOffTransition;
     private boolean mPerformScreenOffTransition = false;
     private int mDozeStateOverride = Display.STATE_UNKNOWN;
     private int mDozeStateOverrideReason = Display.STATE_REASON_UNKNOWN;
 
-    public DisplayStateController(DisplayPowerProximityStateController
-            displayPowerProximityStateController) {
+    public DisplayStateController(
+            DisplayPowerProximityStateController displayPowerProximityStateController,
+            boolean shouldSkipScreenOffTransition) {
         this.mDisplayPowerProximityStateController = displayPowerProximityStateController;
+        this.mShouldSkipScreenOffTransition = shouldSkipScreenOffTransition;
     }
 
     /**
@@ -65,7 +68,7 @@
         switch (displayPowerRequest.policy) {
             case DisplayManagerInternal.DisplayPowerRequest.POLICY_OFF:
                 state = Display.STATE_OFF;
-                mPerformScreenOffTransition = true;
+                mPerformScreenOffTransition = !mShouldSkipScreenOffTransition;
                 break;
             case DisplayManagerInternal.DisplayPowerRequest.POLICY_DOZE:
                 if (mDozeStateOverride != Display.STATE_UNKNOWN) {
@@ -117,7 +120,8 @@
     public void dump(PrintWriter pw) {
         pw.println("DisplayStateController:");
         pw.println("-----------------------");
-        pw.println("  mPerformScreenOffTransition:" + mPerformScreenOffTransition);
+        pw.println("  mShouldSkipScreenOffTransition=" + mShouldSkipScreenOffTransition);
+        pw.println("  mPerformScreenOffTransition=" + mPerformScreenOffTransition);
         pw.println("  mDozeStateOverride=" + mDozeStateOverride);
 
         IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecFeatureAction.java b/services/core/java/com/android/server/hdmi/HdmiCecFeatureAction.java
index 236333e..18ae044 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecFeatureAction.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecFeatureAction.java
@@ -303,6 +303,10 @@
         return mSource.getDeviceInfo().getPhysicalAddress();
     }
 
+    protected final int getServicePath() {
+        return mService.getPhysicalAddress();
+    }
+
     protected final void sendUserControlPressedAndReleased(int targetAddress, int uiCommand) {
         mSource.sendUserControlPressedAndReleased(targetAddress, uiCommand);
     }
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 7505c71..424102c 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -212,11 +212,11 @@
                     HdmiConfig.TIMEOUT_MS);
         }
 
-        launchRoutingControl(reason != HdmiControlService.INITIATED_BY_ENABLE_CEC &&
-                reason != HdmiControlService.INITIATED_BY_BOOT_UP);
         resetSelectRequestBuffer();
         launchDeviceDiscovery();
         startQueuedActions();
+        final boolean routingForBootup = reason != HdmiControlService.INITIATED_BY_ENABLE_CEC
+                && reason != HdmiControlService.INITIATED_BY_BOOT_UP;
         List<HdmiCecMessage> bufferedActiveSource = mDelayedMessageBuffer
                 .getBufferedMessagesWithOpcode(Constants.MESSAGE_ACTIVE_SOURCE);
         if (bufferedActiveSource.isEmpty()) {
@@ -227,14 +227,8 @@
             addAndStartAction(new RequestActiveSourceAction(this, new IHdmiControlCallback.Stub() {
                 @Override
                 public void onComplete(int result) {
-                    if (!mService.getLocalActiveSource().isValid()
-                            && result != HdmiControlManager.RESULT_SUCCESS) {
-                        mService.sendCecCommand(HdmiCecMessageBuilder.buildActiveSource(
-                                getDeviceInfo().getLogicalAddress(),
-                                getDeviceInfo().getPhysicalAddress()));
-                        updateActiveSource(getDeviceInfo().getLogicalAddress(),
-                                getDeviceInfo().getPhysicalAddress(),
-                                "RequestActiveSourceAction#finishWithCallback()");
+                    if (result != HdmiControlManager.RESULT_SUCCESS) {
+                        launchRoutingControl(routingForBootup);
                     }
                 }
             }));
@@ -1384,8 +1378,7 @@
         } else {
             int activePath = mService.getPhysicalAddress();
             setActivePath(activePath);
-            if (!routingForBootup
-                    && !mDelayedMessageBuffer.isBuffered(Constants.MESSAGE_ACTIVE_SOURCE)) {
+            if (!mDelayedMessageBuffer.isBuffered(Constants.MESSAGE_ACTIVE_SOURCE)) {
                 mService.sendCecCommand(
                         HdmiCecMessageBuilder.buildActiveSource(
                                 getDeviceInfo().getLogicalAddress(), activePath));
diff --git a/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java b/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java
index 256905d..9f6322d9 100644
--- a/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java
+++ b/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java
@@ -152,7 +152,8 @@
         // If the device wasn´t the active source yet,
         // this makes it the active source and wakes it up.
         mSource.mService.setAndBroadcastActiveSourceFromOneDeviceType(
-                mTargetAddress, getSourcePath(), "OneTouchPlayAction#broadcastActiveSource()");
+                mTargetAddress, getServicePath(),
+                "OneTouchPlayAction#broadcastActiveSource()");
         // When OneTouchPlay is called, client side should be responsible to send out the intent
         // of which internal source, for example YouTube, it would like to switch to.
         // Here we only update the active port and the active source records in the local
diff --git a/services/core/java/com/android/server/input/InputGestureManager.java b/services/core/java/com/android/server/input/InputGestureManager.java
index 73d5630..8681ea5 100644
--- a/services/core/java/com/android/server/input/InputGestureManager.java
+++ b/services/core/java/com/android/server/input/InputGestureManager.java
@@ -221,6 +221,18 @@
             systemShortcuts.add(createKeyGesture(KeyEvent.KEYCODE_EQUALS,
                     KeyEvent.META_META_ON | KeyEvent.META_ALT_ON,
                     KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFIER_ZOOM_IN));
+            systemShortcuts.add(createKeyGesture(KeyEvent.KEYCODE_DPAD_LEFT,
+                    KeyEvent.META_CTRL_ON | KeyEvent.META_ALT_ON,
+                    KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFIER_PAN_LEFT));
+            systemShortcuts.add(createKeyGesture(KeyEvent.KEYCODE_DPAD_RIGHT,
+                    KeyEvent.META_CTRL_ON | KeyEvent.META_ALT_ON,
+                    KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFIER_PAN_RIGHT));
+            systemShortcuts.add(createKeyGesture(KeyEvent.KEYCODE_DPAD_UP,
+                    KeyEvent.META_CTRL_ON | KeyEvent.META_ALT_ON,
+                    KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFIER_PAN_UP));
+            systemShortcuts.add(createKeyGesture(KeyEvent.KEYCODE_DPAD_DOWN,
+                    KeyEvent.META_CTRL_ON | KeyEvent.META_ALT_ON,
+                    KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFIER_PAN_DOWN));
             systemShortcuts.add(createKeyGesture(KeyEvent.KEYCODE_M,
                     KeyEvent.META_META_ON | KeyEvent.META_ALT_ON,
                     KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MAGNIFICATION));
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index aee5e7f..559b4ae 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -34,6 +34,7 @@
 import android.annotation.Nullable;
 import android.annotation.PermissionManuallyEnforced;
 import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
 import android.annotation.UserIdInt;
 import android.app.ActivityManagerInternal;
 import android.bluetooth.BluetoothAdapter;
@@ -2625,38 +2626,14 @@
         return mWindowManagerCallbacks.interceptUnhandledKey(event, focus);
     }
 
+    @SuppressLint("MissingPermission")
     private void initKeyGestures() {
         InputManager im = Objects.requireNonNull(mContext.getSystemService(InputManager.class));
         im.registerKeyGestureEventHandler(new InputManager.KeyGestureEventHandler() {
             @Override
             public boolean handleKeyGestureEvent(@NonNull KeyGestureEvent event,
                     @Nullable IBinder focussedToken) {
-                int deviceId = event.getDeviceId();
-                boolean complete = event.getAction() == KeyGestureEvent.ACTION_GESTURE_COMPLETE
-                        && !event.isCancelled();
-                switch (event.getKeyGestureType()) {
-                    case KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_UP:
-                        if (complete) {
-                            mKeyboardBacklightController.incrementKeyboardBacklight(deviceId);
-                        }
-                        return true;
-                    case KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_DOWN:
-                        if (complete) {
-                            mKeyboardBacklightController.decrementKeyboardBacklight(deviceId);
-                        }
-                        return true;
-                    case KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_TOGGLE:
-                        // TODO(b/367748270): Add functionality to turn keyboard backlight on/off.
-                        return true;
-                    case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK:
-                        if (complete) {
-                            mNative.toggleCapsLock(deviceId);
-                        }
-                        return true;
-                    default:
-                        return false;
-
-                }
+                return InputManagerService.this.handleKeyGestureEvent(event);
             }
 
             @Override
@@ -2666,6 +2643,10 @@
                     case KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_DOWN:
                     case KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_TOGGLE:
                     case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK:
+                    case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_SLOW_KEYS:
+                    case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_BOUNCE_KEYS:
+                    case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MOUSE_KEYS:
+                    case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_STICKY_KEYS:
                         return true;
                     default:
                         return false;
@@ -2675,6 +2656,73 @@
         });
     }
 
+    @SuppressLint("MissingPermission")
+    @VisibleForTesting
+    boolean handleKeyGestureEvent(@NonNull KeyGestureEvent event) {
+        int deviceId = event.getDeviceId();
+        boolean complete = event.getAction() == KeyGestureEvent.ACTION_GESTURE_COMPLETE
+                && !event.isCancelled();
+        switch (event.getKeyGestureType()) {
+            case KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_UP:
+                if (complete) {
+                    mKeyboardBacklightController.incrementKeyboardBacklight(deviceId);
+                }
+                return true;
+            case KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_DOWN:
+                if (complete) {
+                    mKeyboardBacklightController.decrementKeyboardBacklight(deviceId);
+                }
+                return true;
+            case KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_TOGGLE:
+                // TODO(b/367748270): Add functionality to turn keyboard backlight on/off.
+                return true;
+            case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK:
+                if (complete) {
+                    mNative.toggleCapsLock(deviceId);
+                }
+                return true;
+            case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_BOUNCE_KEYS:
+                if (complete && InputSettings.isAccessibilityBounceKeysFeatureEnabled()) {
+                    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 (complete && InputSettings.isAccessibilityMouseKeysFeatureFlagEnabled()) {
+                    final boolean mouseKeysEnabled = InputSettings.isAccessibilityMouseKeysEnabled(
+                            mContext);
+                    InputSettings.setAccessibilityMouseKeysEnabled(mContext, !mouseKeysEnabled);
+                    return true;
+                }
+                break;
+            case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_STICKY_KEYS:
+                if (complete && InputSettings.isAccessibilityStickyKeysFeatureEnabled()) {
+                    final boolean stickyKeysEnabled =
+                            InputSettings.isAccessibilityStickyKeysEnabled(mContext);
+                    InputSettings.setAccessibilityStickyKeysEnabled(mContext, !stickyKeysEnabled);
+                    return true;
+                }
+                break;
+            case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_SLOW_KEYS:
+                if (complete && InputSettings.isAccessibilitySlowKeysFeatureFlagEnabled()) {
+                    final boolean slowKeysEnabled =
+                            InputSettings.isAccessibilitySlowKeysEnabled(mContext);
+                    InputSettings.setAccessibilitySlowKeysThreshold(mContext,
+                            slowKeysEnabled ? 0 : InputSettings.DEFAULT_SLOW_KEYS_THRESHOLD_MILLIS);
+                    return true;
+                }
+                break;
+            default:
+                return false;
+
+        }
+        return false;
+    }
+
     // Native callback.
     @SuppressWarnings("unused")
     private void onPointerDownOutsideFocus(IBinder touchedToken) {
diff --git a/services/core/java/com/android/server/input/KeyGestureController.java b/services/core/java/com/android/server/input/KeyGestureController.java
index 99c01ce..5f7ad27 100644
--- a/services/core/java/com/android/server/input/KeyGestureController.java
+++ b/services/core/java/com/android/server/input/KeyGestureController.java
@@ -801,7 +801,15 @@
                         + " interceptKeyBeforeQueueing");
                 return true;
             case KeyEvent.KEYCODE_DO_NOT_DISTURB:
-                // TODO(b/365920375): Implement 25Q2 keycode implementation in system
+                if (enableNew25q2Keycodes()) {
+                    if (firstDown) {
+                        handleKeyGesture(deviceId, new int[]{KeyEvent.KEYCODE_DO_NOT_DISTURB},
+                                /* modifierState = */0,
+                                KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_DO_NOT_DISTURB,
+                                KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId, focusedToken,
+                                /* flags = */0, /* appLaunchData = */null);
+                    }
+                }
                 return true;
         }
 
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 02dd884..c653dec 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -1209,8 +1209,14 @@
         // Hide soft input before user switch task since switch task may block main handler a while
         // and delayed the hideCurrentInputLocked().
         final var userData = getUserData(userId);
-        hideCurrentInputLocked(userData.mImeBindingState.mFocusedWindow, 0 /* flags */,
-                SoftInputShowHideReason.HIDE_SWITCH_USER, userId);
+        if (Flags.refactorInsetsController()) {
+            final var statsToken = createStatsTokenForFocusedClient(false /* show */,
+                    SoftInputShowHideReason.HIDE_SWITCH_USER, userId);
+            setImeVisibilityOnFocusedWindowClient(false, userData, statsToken);
+        } else {
+            hideCurrentInputLocked(userData.mImeBindingState.mFocusedWindow, 0 /* flags */,
+                    SoftInputShowHideReason.HIDE_SWITCH_USER, userId);
+        }
         final UserSwitchHandlerTask task = new UserSwitchHandlerTask(this, userId,
                 clientToBeReset);
         mUserSwitchHandlerTask = task;
@@ -2717,17 +2723,7 @@
             return false;
         }
 
-        final InputMethodSettings settings = InputMethodSettingsRepository.get(userId);
-        if (Flags.imeSwitcherRevamp()) {
-            // The IME switcher button should be shown when the current IME specified a
-            // language settings activity.
-            final var curImi = settings.getMethodMap().get(settings.getSelectedInputMethod());
-            if (curImi != null && curImi.createImeLanguageSettingsActivityIntent() != null) {
-                return true;
-            }
-        }
-
-        return hasMultipleSubtypesForSwitcher(false /* nonAuxOnly */, settings);
+        return hasMultipleSubtypesForSwitcher(false /* nonAuxOnly */, userId);
     }
 
     /**
@@ -2735,10 +2731,11 @@
      * across all enabled IMEs for the given user.
      *
      * @param nonAuxOnly whether to check only for non auxiliary subtypes.
-     * @param settings   the input method settings under the given user ID.
+     * @param userId     the id of the user for which to check the number of subtypes.
      */
     private static boolean hasMultipleSubtypesForSwitcher(boolean nonAuxOnly,
-            @NonNull InputMethodSettings settings) {
+            @UserIdInt int userId) {
+        final var settings = InputMethodSettingsRepository.get(userId);
         List<InputMethodInfo> imes = settings.getEnabledInputMethodListWithFilter(
                 InputMethodInfo::shouldShowInInputMethodPicker);
         final int numImes = imes.size();
@@ -4124,8 +4121,7 @@
     @GuardedBy("ImfLock.class")
     private void onImeSwitchButtonClickLocked(int displayId, @NonNull UserData userData) {
         final int userId = userData.mUserId;
-        final var settings = InputMethodSettingsRepository.get(userId);
-        if (hasMultipleSubtypesForSwitcher(true /* nonAuxOnly */, settings)) {
+        if (hasMultipleSubtypesForSwitcher(true /* nonAuxOnly */, userId)) {
             switchToNextInputMethodLocked(false /* onlyCurrentIme */, userData);
         } else {
             showInputMethodPickerFromSystem(
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java b/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java
index 556cc03..d29fde2 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java
@@ -16,7 +16,6 @@
 
 package com.android.server.location.contexthub;
 
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.hardware.location.ContextHubManager.AUTHORIZATION_DENIED;
 import static android.hardware.location.ContextHubManager.AUTHORIZATION_DENIED_GRACE_PERIOD;
 import static android.hardware.location.ContextHubManager.AUTHORIZATION_GRANTED;
@@ -25,7 +24,6 @@
 import android.annotation.Nullable;
 import android.app.AppOpsManager;
 import android.app.PendingIntent;
-import android.chre.flags.Flags;
 import android.compat.Compatibility;
 import android.compat.annotation.ChangeId;
 import android.compat.annotation.EnabledAfter;
@@ -655,7 +653,13 @@
         // If in the grace period, don't check permissions state since it'll cause cleanup
         // messages to be dropped.
         if (authState == AUTHORIZATION_DENIED
-                || !notePermissions(messagePermissions, RECEIVE_MSG_NOTE + nanoAppId)) {
+                || !ContextHubServiceUtil.notePermissions(
+                        mAppOpsManager,
+                        mUid,
+                        mPackage,
+                        mAttributionTag,
+                        messagePermissions,
+                        RECEIVE_MSG_NOTE + nanoAppId)) {
             Log.e(TAG, "Dropping message from " + Long.toHexString(nanoAppId) + ". " + mPackage
                     + " doesn't have permission");
             return ErrorCode.PERMISSION_DENIED;
@@ -754,56 +758,6 @@
     }
 
     /**
-     * Checks that this client has all of the provided permissions.
-     *
-     * @param permissions list of permissions to check
-     * @return true if the client has all of the permissions granted
-     */
-    boolean hasPermissions(List<String> permissions) {
-        for (String permission : permissions) {
-            if (mContext.checkPermission(permission, mPid, mUid) != PERMISSION_GRANTED) {
-                Log.e(TAG, "no permission for " + permission);
-                return false;
-            }
-        }
-        return true;
-    }
-
-    /**
-     * Attributes the provided permissions to the package of this client.
-     *
-     * @param permissions list of permissions covering data the client is about to receive
-     * @param noteMessage message that should be noted alongside permissions attribution to
-     *     facilitate debugging
-     * @return true if client has ability to use all of the provided permissions
-     */
-    boolean notePermissions(List<String> permissions, String noteMessage) {
-        for (String permission : permissions) {
-            int opCode = AppOpsManager.permissionToOpCode(permission);
-            if (opCode != AppOpsManager.OP_NONE) {
-                try {
-                    if (mAppOpsManager.noteOp(opCode, mUid, mPackage, mAttributionTag, noteMessage)
-                            != AppOpsManager.MODE_ALLOWED) {
-                        return false;
-                    }
-                } catch (SecurityException e) {
-                    Log.e(
-                            TAG,
-                            "SecurityException: noteOp for pkg "
-                                    + mPackage
-                                    + " opcode "
-                                    + opCode
-                                    + ": "
-                                    + e.getMessage());
-                    return false;
-                }
-            }
-        }
-
-        return true;
-    }
-
-    /**
      * @return true if the client is a PendingIntent client that has been cancelled.
      */
     boolean isPendingIntentCancelled() {
@@ -868,7 +822,8 @@
         synchronized (mMessageChannelNanoappIdMap) {
             // Check permission granted state synchronously since this method can be invoked from
             // multiple threads.
-            boolean hasPermissions = hasPermissions(nanoappPermissions);
+            boolean hasPermissions =
+                    ContextHubServiceUtil.hasPermissions(mContext, mPid, mUid, nanoappPermissions);
 
             curAuthState = mMessageChannelNanoappIdMap.getOrDefault(
                     nanoAppId, AUTHORIZATION_UNKNOWN);
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java b/services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java
index ef73463..2c072d0 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java
@@ -18,10 +18,14 @@
 
 import android.content.Context;
 import android.hardware.contexthub.EndpointInfo;
+import android.hardware.contexthub.ErrorCode;
 import android.hardware.contexthub.HubEndpointInfo;
 import android.hardware.contexthub.HubMessage;
 import android.hardware.contexthub.IContextHubEndpoint;
 import android.hardware.contexthub.IContextHubEndpointCallback;
+import android.hardware.contexthub.Message;
+import android.hardware.contexthub.MessageDeliveryStatus;
+import android.hardware.location.ContextHubTransaction;
 import android.hardware.location.IContextHubTransactionCallback;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -78,18 +82,28 @@
     @GuardedBy("mOpenSessionLock")
     private final Set<Integer> mActiveRemoteSessionIds = new HashSet<>();
 
+    /** The package name of the app that created the endpoint */
+    private final String mPackageName;
+
+    /* Transaction manager used for sending reliable messages */
+    private final ContextHubTransactionManager mTransactionManager;
+
     /* package */ ContextHubEndpointBroker(
             Context context,
             IContextHubWrapper contextHubProxy,
             ContextHubEndpointManager endpointManager,
             EndpointInfo halEndpointInfo,
-            IContextHubEndpointCallback callback) {
+            IContextHubEndpointCallback callback,
+            String packageName,
+            ContextHubTransactionManager transactionManager) {
         mContext = context;
         mContextHubProxy = contextHubProxy;
         mEndpointManager = endpointManager;
         mEndpointInfo = new HubEndpointInfo(halEndpointInfo);
         mHalEndpointInfo = halEndpointInfo;
         mContextHubEndpointCallback = callback;
+        mPackageName = packageName;
+        mTransactionManager = transactionManager;
     }
 
     @Override
@@ -98,9 +112,10 @@
     }
 
     @Override
+    @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
     public int openSession(HubEndpointInfo destination, String serviceDescriptor)
             throws RemoteException {
-        ContextHubServiceUtil.checkPermissions(mContext);
+        super.openSession_enforcePermission();
         if (!mIsRegistered.get()) throw new IllegalStateException("Endpoint is not registered");
         int sessionId = mEndpointManager.reserveSessionId();
         EndpointInfo halEndpointInfo = ContextHubServiceUtil.convertHalEndpointInfo(destination);
@@ -125,11 +140,13 @@
     }
 
     @Override
+    @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
     public void closeSession(int sessionId, int reason) throws RemoteException {
-        ContextHubServiceUtil.checkPermissions(mContext);
+        super.closeSession_enforcePermission();
         if (!mIsRegistered.get()) throw new IllegalStateException("Endpoint is not registered");
         try {
-            mContextHubProxy.closeEndpointSession(sessionId, (byte) reason);
+            mContextHubProxy.closeEndpointSession(
+                    sessionId, ContextHubServiceUtil.toHalReason(reason));
         } catch (RemoteException | IllegalArgumentException | UnsupportedOperationException e) {
             Log.e(TAG, "Exception while calling HAL closeEndpointSession", e);
             throw e;
@@ -137,8 +154,9 @@
     }
 
     @Override
+    @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
     public void unregister() {
-        ContextHubServiceUtil.checkPermissions(mContext);
+        super.unregister_enforcePermission();
         mIsRegistered.set(false);
         try {
             mContextHubProxy.unregisterEndpoint(mHalEndpointInfo);
@@ -160,8 +178,9 @@
     }
 
     @Override
+    @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
     public void openSessionRequestComplete(int sessionId) {
-        ContextHubServiceUtil.checkPermissions(mContext);
+        super.openSessionRequestComplete_enforcePermission();
         synchronized (mOpenSessionLock) {
             try {
                 mContextHubProxy.endpointSessionOpenComplete(sessionId);
@@ -173,14 +192,61 @@
     }
 
     @Override
+    @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
     public void sendMessage(
             int sessionId, HubMessage message, IContextHubTransactionCallback callback) {
-        // TODO(b/381102453): Implement this
+        super.sendMessage_enforcePermission();
+        Message halMessage = ContextHubServiceUtil.createHalMessage(message);
+        synchronized (mOpenSessionLock) {
+            if (!mActiveSessionIds.contains(sessionId)
+                    && !mActiveRemoteSessionIds.contains(sessionId)) {
+                throw new SecurityException(
+                        "sendMessage called on inactive session (id= " + sessionId + ")");
+            }
+        }
+
+        // TODO(b/381102453): Handle permissions
+        if (callback == null) {
+            try {
+                mContextHubProxy.sendMessageToEndpoint(sessionId, halMessage);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Exception while sending message on session " + sessionId, e);
+            }
+        } else {
+            ContextHubServiceTransaction transaction =
+                    mTransactionManager.createSessionMessageTransaction(
+                            sessionId, halMessage, mPackageName, callback);
+            try {
+                mTransactionManager.addTransaction(transaction);
+            } catch (IllegalStateException e) {
+                Log.e(
+                        TAG,
+                        "Unable to add a transaction in sendMessageToEndpoint "
+                                + "(session ID = "
+                                + sessionId
+                                + ")",
+                        e);
+                transaction.onTransactionComplete(
+                        ContextHubTransaction.RESULT_FAILED_SERVICE_INTERNAL_FAILURE);
+            }
+        }
     }
 
     @Override
+    @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
     public void sendMessageDeliveryStatus(int sessionId, int messageSeqNumber, byte errorCode) {
-        // TODO(b/381102453): Implement this
+        super.sendMessageDeliveryStatus_enforcePermission();
+        MessageDeliveryStatus status = new MessageDeliveryStatus();
+        status.messageSequenceNumber = messageSeqNumber;
+        status.errorCode = errorCode;
+        try {
+            mContextHubProxy.sendMessageDeliveryStatusToEndpoint(sessionId, status);
+        } catch (RemoteException e) {
+            Log.w(
+                    TAG,
+                    "Exception while sending message delivery status on session " + sessionId,
+                    e);
+        }
     }
 
     /** Invoked when the underlying binder of this broker has died at the client process. */
@@ -217,7 +283,8 @@
         }
         if (mContextHubEndpointCallback != null) {
             try {
-                mContextHubEndpointCallback.onSessionClosed(sessionId, reason);
+                mContextHubEndpointCallback.onSessionClosed(
+                        sessionId, ContextHubServiceUtil.toAppHubEndpointReason(reason));
             } catch (RemoteException e) {
                 Log.e(TAG, "RemoteException while calling onSessionClosed", e);
             }
@@ -238,6 +305,21 @@
         }
     }
 
+    /* package */ void onMessageReceived(int sessionId, HubMessage message) {
+        if (mContextHubEndpointCallback != null) {
+            try {
+                mContextHubEndpointCallback.onMessageReceived(sessionId, message);
+            } catch (RemoteException e) {
+                Log.e(TAG, "RemoteException while calling onMessageReceived", e);
+            }
+        }
+    }
+
+    /* package */ void onMessageDeliveryStatusReceived(
+            int sessionId, int sequenceNumber, byte errorCode) {
+        mTransactionManager.onMessageDeliveryResponse(sequenceNumber, errorCode == ErrorCode.OK);
+    }
+
     /* package */ boolean hasSessionId(int sessionId) {
         synchronized (mOpenSessionLock) {
             return mPendingSessionIds.contains(sessionId)
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubEndpointManager.java b/services/core/java/com/android/server/location/contexthub/ContextHubEndpointManager.java
index 155a92a..07df7f9 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubEndpointManager.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubEndpointManager.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 import android.hardware.contexthub.EndpointInfo;
 import android.hardware.contexthub.HubEndpointInfo;
+import android.hardware.contexthub.HubMessage;
 import android.hardware.contexthub.IContextHubEndpoint;
 import android.hardware.contexthub.IContextHubEndpointCallback;
 import android.os.RemoteException;
@@ -59,6 +60,8 @@
 
     private final HubInfoRegistry mHubInfoRegistry;
 
+    private final ContextHubTransactionManager mTransactionManager;
+
     /** A map of endpoint IDs to brokers currently registered. */
     private final Map<Long, ContextHubEndpointBroker> mEndpointMap = new ConcurrentHashMap<>();
 
@@ -93,10 +96,14 @@
     private final boolean mSessionIdsValid;
 
     /* package */ ContextHubEndpointManager(
-            Context context, IContextHubWrapper contextHubProxy, HubInfoRegistry hubInfoRegistry) {
+            Context context,
+            IContextHubWrapper contextHubProxy,
+            HubInfoRegistry hubInfoRegistry,
+            ContextHubTransactionManager transactionManager) {
         mContext = context;
         mContextHubProxy = contextHubProxy;
         mHubInfoRegistry = hubInfoRegistry;
+        mTransactionManager = transactionManager;
         int[] range = null;
         try {
             range = mContextHubProxy.requestSessionIdRange(SERVICE_SESSION_RANGE);
@@ -132,11 +139,14 @@
      *
      * @param pendingEndpointInfo the object describing the endpoint being registered
      * @param callback the callback interface of the endpoint to register
+     * @param packageName the name of the package of the calling client
      * @return the endpoint interface
      * @throws IllegalStateException if max number of endpoints have already registered
      */
     /* package */ IContextHubEndpoint registerEndpoint(
-            HubEndpointInfo pendingEndpointInfo, IContextHubEndpointCallback callback)
+            HubEndpointInfo pendingEndpointInfo,
+            IContextHubEndpointCallback callback,
+            String packageName)
             throws RemoteException {
         if (!mSessionIdsValid) {
             throw new IllegalStateException("ContextHubEndpointManager failed to initialize");
@@ -158,7 +168,9 @@
                         mContextHubProxy,
                         this /* endpointManager */,
                         halEndpointInfo,
-                        callback);
+                        callback,
+                        packageName,
+                        mTransactionManager);
         mEndpointMap.put(endpointId, broker);
 
         try {
@@ -283,6 +295,38 @@
         }
     }
 
+    @Override
+    public void onMessageReceived(int sessionId, HubMessage message) {
+        boolean callbackInvoked = false;
+        for (ContextHubEndpointBroker broker : mEndpointMap.values()) {
+            if (broker.hasSessionId(sessionId)) {
+                broker.onMessageReceived(sessionId, message);
+                callbackInvoked = true;
+                break;
+            }
+        }
+
+        if (!callbackInvoked) {
+            Log.w(TAG, "onMessageReceived: unknown session ID " + sessionId);
+        }
+    }
+
+    @Override
+    public void onMessageDeliveryStatusReceived(int sessionId, int sequenceNumber, byte errorCode) {
+        boolean callbackInvoked = false;
+        for (ContextHubEndpointBroker broker : mEndpointMap.values()) {
+            if (broker.hasSessionId(sessionId)) {
+                broker.onMessageDeliveryStatusReceived(sessionId, sequenceNumber, errorCode);
+                callbackInvoked = true;
+                break;
+            }
+        }
+
+        if (!callbackInvoked) {
+            Log.w(TAG, "onMessageDeliveryStatusReceived: unknown session ID " + sessionId);
+        }
+    }
+
     /** @return an available endpoint ID */
     private long getNewEndpointId() {
         synchronized (mEndpointLock) {
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubHalEndpointCallback.java b/services/core/java/com/android/server/location/contexthub/ContextHubHalEndpointCallback.java
index 9d52c6a..f1f2217 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubHalEndpointCallback.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubHalEndpointCallback.java
@@ -17,6 +17,7 @@
 
 import android.hardware.contexthub.EndpointId;
 import android.hardware.contexthub.HubEndpointInfo;
+import android.hardware.contexthub.HubMessage;
 import android.hardware.contexthub.IEndpointCallback;
 import android.hardware.contexthub.Message;
 import android.hardware.contexthub.MessageDeliveryStatus;
@@ -51,6 +52,12 @@
 
         /** Called when a requested endpoint open session is completed */
         void onEndpointSessionOpenComplete(int sessionId);
+
+        /** Called when a message is received for the session */
+        void onMessageReceived(int sessionId, HubMessage message);
+
+        /** Called when a message delivery status is received for the session */
+        void onMessageDeliveryStatusReceived(int sessionId, int sequenceNumber, byte errorCode);
     }
 
     ContextHubHalEndpointCallback(
@@ -84,13 +91,6 @@
     }
 
     @Override
-    public void onMessageReceived(int i, Message message) throws RemoteException {}
-
-    @Override
-    public void onMessageDeliveryStatusReceived(int i, MessageDeliveryStatus messageDeliveryStatus)
-            throws RemoteException {}
-
-    @Override
     public void onEndpointSessionOpenRequest(
             int i, EndpointId destination, EndpointId initiator, String s) throws RemoteException {
         HubEndpointInfo.HubEndpointIdentifier destinationId =
@@ -111,6 +111,19 @@
     }
 
     @Override
+    public void onMessageReceived(int i, Message message) throws RemoteException {
+        HubMessage hubMessage = ContextHubServiceUtil.createHubMessage(message);
+        mEndpointSessionCallback.onMessageReceived(i, hubMessage);
+    }
+
+    @Override
+    public void onMessageDeliveryStatusReceived(int i, MessageDeliveryStatus messageDeliveryStatus)
+            throws RemoteException {
+        mEndpointSessionCallback.onMessageDeliveryStatusReceived(
+                i, messageDeliveryStatus.messageSequenceNumber, messageDeliveryStatus.errorCode);
+    }
+
+    @Override
     public int getInterfaceVersion() throws RemoteException {
         return IEndpointCallback.VERSION;
     }
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubService.java b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
index d916eda..165f9d3 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubService.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
@@ -334,7 +334,8 @@
             try {
                 registry = new HubInfoRegistry(mContextHubWrapper);
                 mEndpointManager =
-                        new ContextHubEndpointManager(mContext, mContextHubWrapper, registry);
+                        new ContextHubEndpointManager(
+                                mContext, mContextHubWrapper, registry, mTransactionManager);
                 Log.i(TAG, "Enabling generic offload API");
             } catch (UnsupportedOperationException e) {
                 mEndpointManager = null;
@@ -794,14 +795,16 @@
     @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
     @Override
     public IContextHubEndpoint registerEndpoint(
-            HubEndpointInfo pendingHubEndpointInfo, IContextHubEndpointCallback callback)
+            HubEndpointInfo pendingHubEndpointInfo,
+            IContextHubEndpointCallback callback,
+            String packageName)
             throws RemoteException {
         super.registerEndpoint_enforcePermission();
         if (mEndpointManager == null) {
             Log.e(TAG, "Endpoint manager failed to initialize");
             throw new UnsupportedOperationException("Endpoint registration is not supported");
         }
-        return mEndpointManager.registerEndpoint(pendingHubEndpointInfo, callback);
+        return mEndpointManager.registerEndpoint(pendingHubEndpointInfo, callback, packageName);
     }
 
     @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubServiceTransaction.java b/services/core/java/com/android/server/location/contexthub/ContextHubServiceTransaction.java
index 3aea6d5..4e96b44 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubServiceTransaction.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubServiceTransaction.java
@@ -16,6 +16,7 @@
 
 package com.android.server.location.contexthub;
 
+import android.chre.flags.Flags;
 import android.hardware.location.ContextHubTransaction;
 import android.hardware.location.NanoAppState;
 
@@ -46,7 +47,11 @@
     /** The number of times the transaction has been started (start function called). */
     private int mNumCompletedStartCalls;
 
-    private final short mHostEndpointId;
+    /**
+     * A unique identifier for the entity which owns this transaction, scoped by the transaction
+     * type.
+     */
+    private final int mOwnerId;
 
     private boolean mIsComplete = false;
 
@@ -59,7 +64,7 @@
         mNextRetryTime = Long.MAX_VALUE;
         mTimeoutTime = Long.MAX_VALUE;
         mNumCompletedStartCalls = 0;
-        mHostEndpointId = Short.MAX_VALUE;
+        mOwnerId = Integer.MAX_VALUE;
     }
 
     ContextHubServiceTransaction(int id, int type, long nanoAppId,
@@ -72,11 +77,11 @@
         mNextRetryTime = Long.MAX_VALUE;
         mTimeoutTime = Long.MAX_VALUE;
         mNumCompletedStartCalls = 0;
-        mHostEndpointId = Short.MAX_VALUE;
+        mOwnerId = Integer.MAX_VALUE;
     }
 
-    ContextHubServiceTransaction(int id, int type, String packageName,
-            int messageSequenceNumber, short hostEndpointId) {
+    ContextHubServiceTransaction(
+            int id, int type, String packageName, int messageSequenceNumber, int ownerId) {
         mTransactionId = id;
         mTransactionType = type;
         mNanoAppId = Long.MAX_VALUE;
@@ -85,7 +90,7 @@
         mNextRetryTime = Long.MAX_VALUE;
         mTimeoutTime = Long.MAX_VALUE;
         mNumCompletedStartCalls = 0;
-        mHostEndpointId = hostEndpointId;
+        mOwnerId = ownerId;
     }
 
     /**
@@ -147,8 +152,15 @@
         return mNumCompletedStartCalls;
     }
 
-    short getHostEndpointId() {
-        return mHostEndpointId;
+    /**
+     * @return A unique identifier for the entity owning this transaction.
+     */
+    long getOwnerId() {
+        if (Flags.offloadImplementation()) {
+            return ((long) mTransactionType << 32) | (0x00000000FFFFFFFFL & mOwnerId);
+        } else {
+            return mOwnerId;
+        }
     }
 
     /**
@@ -215,15 +227,16 @@
             out.append(", messageSequenceNumber = ");
             out.append(mMessageSequenceNumber);
         }
-        if (mTransactionType == ContextHubTransaction.TYPE_RELIABLE_MESSAGE) {
+        if (mTransactionType == ContextHubTransaction.TYPE_RELIABLE_MESSAGE
+                || mTransactionType == ContextHubTransaction.TYPE_HUB_MESSAGE_REQUIRES_RESPONSE) {
             out.append(", nextRetryTime = ");
             out.append(mNextRetryTime);
             out.append(", timeoutTime = ");
             out.append(mTimeoutTime);
             out.append(", numCompletedStartCalls = ");
             out.append(mNumCompletedStartCalls);
-            out.append(", hostEndpointId = ");
-            out.append(mHostEndpointId);
+            out.append(", ownerId = ");
+            out.append(getOwnerId());
         }
         out.append(")");
 
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubServiceUtil.java b/services/core/java/com/android/server/location/contexthub/ContextHubServiceUtil.java
index 05be427..957307a 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubServiceUtil.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubServiceUtil.java
@@ -16,11 +16,18 @@
 
 package com.android.server.location.contexthub;
 
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+
 import android.Manifest;
+import android.app.AppOpsManager;
 import android.content.Context;
 import android.hardware.contexthub.EndpointInfo;
+import android.hardware.contexthub.HubEndpoint;
 import android.hardware.contexthub.HubEndpointInfo;
+import android.hardware.contexthub.HubMessage;
 import android.hardware.contexthub.HubServiceInfo;
+import android.hardware.contexthub.Message;
+import android.hardware.contexthub.Reason;
 import android.hardware.contexthub.V1_0.AsyncEventType;
 import android.hardware.contexthub.V1_0.ContextHubMsg;
 import android.hardware.contexthub.V1_0.HostEndPoint;
@@ -465,4 +472,163 @@
         }
         return outputInfo;
     }
+
+    /**
+     * Converts a HubMessage object to a AIDL HAL Message object.
+     *
+     * @param message the HubMessage message to convert
+     * @return the AIDL HAL message
+     */
+    /* package */
+    static Message createHalMessage(HubMessage message) {
+        Message outMessage = new Message();
+        outMessage.flags =
+                message.getDeliveryParams().isResponseRequired()
+                        ? Message.FLAG_REQUIRES_DELIVERY_STATUS
+                        : 0;
+        outMessage.permissions = new String[0];
+        outMessage.sequenceNumber = message.getMessageSequenceNumber();
+        outMessage.type = message.getMessageType();
+        outMessage.content = message.getMessageBody();
+        return outMessage;
+    }
+
+    /**
+     * Converts a AIDL HAL Message object to a HubMessage object.
+     *
+     * @param message the AIDL HAL Message message to convert
+     * @return the HubMessage
+     */
+    /* package */
+    static HubMessage createHubMessage(Message message) {
+        boolean isReliable = (message.flags & Message.FLAG_REQUIRES_DELIVERY_STATUS) != 0;
+        return HubMessage.createMessage(
+                message.type,
+                message.content,
+                HubMessage.DeliveryParams.makeBasic().setResponseRequired(isReliable));
+    }
+
+    /**
+     * Converts a byte integer defined by Reason.aidl to HubEndpoint.Reason values exposed to apps.
+     *
+     * @param reason The Reason.aidl value
+     * @return The converted HubEndpoint.Reason value
+     */
+    /* package */
+    static @HubEndpoint.Reason int toAppHubEndpointReason(byte reason) {
+        switch (reason) {
+            case Reason.UNSPECIFIED:
+            case Reason.OUT_OF_MEMORY:
+            case Reason.TIMEOUT:
+                return HubEndpoint.REASON_FAILURE;
+            case Reason.OPEN_ENDPOINT_SESSION_REQUEST_REJECTED:
+                return HubEndpoint.REASON_OPEN_ENDPOINT_SESSION_REQUEST_REJECTED;
+            case Reason.CLOSE_ENDPOINT_SESSION_REQUESTED:
+                return HubEndpoint.REASON_CLOSE_ENDPOINT_SESSION_REQUESTED;
+            case Reason.ENDPOINT_INVALID:
+                return HubEndpoint.REASON_ENDPOINT_INVALID;
+            case Reason.ENDPOINT_GONE:
+            case Reason.ENDPOINT_CRASHED:
+            case Reason.HUB_RESET:
+                return HubEndpoint.REASON_ENDPOINT_STOPPED;
+            case Reason.PERMISSION_DENIED:
+                return HubEndpoint.REASON_PERMISSION_DENIED;
+            default:
+                Log.w(TAG, "toAppHubEndpointReason: invalid reason: " + reason);
+                return HubEndpoint.REASON_FAILURE;
+        }
+    }
+
+    /**
+     * Converts a byte integer defined by Reason.aidl to HubEndpoint.Reason values exposed to apps.
+     *
+     * @param reason The Reason.aidl value
+     * @return The converted HubEndpoint.Reason value
+     */
+    /* package */
+    static byte toHalReason(@HubEndpoint.Reason int reason) {
+        switch (reason) {
+            case HubEndpoint.REASON_FAILURE:
+                return Reason.UNSPECIFIED;
+            case HubEndpoint.REASON_OPEN_ENDPOINT_SESSION_REQUEST_REJECTED:
+                return Reason.OPEN_ENDPOINT_SESSION_REQUEST_REJECTED;
+            case HubEndpoint.REASON_CLOSE_ENDPOINT_SESSION_REQUESTED:
+                return Reason.CLOSE_ENDPOINT_SESSION_REQUESTED;
+            case HubEndpoint.REASON_ENDPOINT_INVALID:
+                return Reason.ENDPOINT_INVALID;
+            case HubEndpoint.REASON_ENDPOINT_STOPPED:
+                return Reason.ENDPOINT_GONE;
+            case HubEndpoint.REASON_PERMISSION_DENIED:
+                return Reason.PERMISSION_DENIED;
+            default:
+                Log.w(TAG, "toHalReason: invalid reason: " + reason);
+                return Reason.UNSPECIFIED;
+        }
+    }
+
+    /**
+     * Checks that the module with the provided context/pid/uid has all of the provided permissions.
+     *
+     * @param context The context to validate permissions for
+     * @param pid The PID to validate permissions for
+     * @param uid The UID to validate permissions for
+     * @param permissions The collection of permissions to check
+     * @return true if the module has all of the permissions granted
+     */
+    /* package */
+    static boolean hasPermissions(
+            Context context, int pid, int uid, Collection<String> permissions) {
+        for (String permission : permissions) {
+            if (context.checkPermission(permission, pid, uid) != PERMISSION_GRANTED) {
+                Log.e(TAG, "no permission for " + permission);
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Attributes the provided permissions to the package of this client.
+     *
+     * @param appOpsManager The app ops manager to use
+     * @param uid The UID of the module to note permissions for
+     * @param packageName The package name of the module to note permissions for
+     * @param attributionTag The attribution tag of the module to note permissions for
+     * @param permissions The list of permissions covering data the client is about to receive
+     * @param noteMessage The message that should be noted alongside permissions attribution to
+     *     facilitate debugging
+     * @return true if client has ability to use all of the provided permissions
+     */
+    /* package */
+    static boolean notePermissions(
+            AppOpsManager appOpsManager,
+            int uid,
+            String packageName,
+            String attributionTag,
+            List<String> permissions,
+            String noteMessage) {
+        for (String permission : permissions) {
+            int opCode = AppOpsManager.permissionToOpCode(permission);
+            if (opCode != AppOpsManager.OP_NONE) {
+                try {
+                    if (appOpsManager.noteOp(opCode, uid, packageName, attributionTag, noteMessage)
+                            != AppOpsManager.MODE_ALLOWED) {
+                        return false;
+                    }
+                } catch (SecurityException e) {
+                    Log.e(
+                            TAG,
+                            "SecurityException: noteOp for pkg "
+                                    + packageName
+                                    + " opcode "
+                                    + opCode
+                                    + ": "
+                                    + e.getMessage());
+                    return false;
+                }
+            }
+        }
+
+        return true;
+    }
 }
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java b/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java
index ccfa61b..5dd40ea 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java
@@ -17,6 +17,7 @@
 package com.android.server.location.contexthub;
 
 import android.chre.flags.Flags;
+import android.hardware.contexthub.Message;
 import android.hardware.location.ContextHubTransaction;
 import android.hardware.location.IContextHubTransactionCallback;
 import android.hardware.location.NanoAppBinary;
@@ -84,9 +85,9 @@
     protected final Map<Integer, ContextHubServiceTransaction> mReliableMessageTransactionMap =
             new HashMap<>();
 
-    /** A set of host endpoint IDs that have an active pending transaction. */
+    /** A set of IDs of transaction owners that have an active pending transaction. */
     @GuardedBy("mReliableMessageLock")
-    protected final Set<Short> mReliableMessageHostEndpointIdActiveSet = new HashSet<>();
+    protected final Set<Long> mReliableMessageOwnerIdActiveSet = new HashSet<>();
 
     protected final AtomicInteger mNextAvailableId = new AtomicInteger();
 
@@ -355,27 +356,32 @@
     /**
      * Creates a transaction to send a reliable message.
      *
-     * @param hostEndpointId      The ID of the host endpoint sending the message.
-     * @param contextHubId        The ID of the hub to send the message to.
-     * @param message             The message to send.
+     * @param ownerId The ID of the transaction owner.
+     * @param contextHubId The ID of the hub to send the message to.
+     * @param message The message to send.
      * @param transactionCallback The callback of the transactions.
-     * @param packageName         The host package associated with this transaction.
+     * @param packageName The host package associated with this transaction.
      * @return The generated transaction.
      */
     /* package */ ContextHubServiceTransaction createMessageTransaction(
-            short hostEndpointId, int contextHubId, NanoAppMessage message,
-            IContextHubTransactionCallback transactionCallback, String packageName) {
-        return new ContextHubServiceTransaction(mNextAvailableId.getAndIncrement(),
-                ContextHubTransaction.TYPE_RELIABLE_MESSAGE, packageName,
-                mNextAvailableMessageSequenceNumber.getAndIncrement(), hostEndpointId) {
+            short ownerId,
+            int contextHubId,
+            NanoAppMessage message,
+            IContextHubTransactionCallback transactionCallback,
+            String packageName) {
+        return new ContextHubServiceTransaction(
+                mNextAvailableId.getAndIncrement(),
+                ContextHubTransaction.TYPE_RELIABLE_MESSAGE,
+                packageName,
+                mNextAvailableMessageSequenceNumber.getAndIncrement(),
+                ownerId) {
             @Override
             /* package */ int onTransact() {
                 try {
                     message.setIsReliable(/* isReliable= */ true);
                     message.setMessageSequenceNumber(getMessageSequenceNumber());
 
-                    return mContextHubProxy.sendMessageToContextHub(hostEndpointId, contextHubId,
-                            message);
+                    return mContextHubProxy.sendMessageToContextHub(ownerId, contextHubId, message);
                 } catch (RemoteException e) {
                     Log.e(TAG, "RemoteException while trying to send a reliable message", e);
                     return ContextHubTransaction.RESULT_FAILED_UNKNOWN;
@@ -394,6 +400,48 @@
     }
 
     /**
+     * Creates a transaction to send a message through a session.
+     *
+     * @param sessionId The ID of the endpoint session the message should be sent through.
+     * @param message The message to send.
+     * @param transactionCallback The callback of the transactions.
+     * @return The generated transaction.
+     */
+    /* package */ ContextHubServiceTransaction createSessionMessageTransaction(
+            int sessionId,
+            Message message,
+            String packageName,
+            IContextHubTransactionCallback transactionCallback) {
+        return new ContextHubServiceTransaction(
+                mNextAvailableId.getAndIncrement(),
+                ContextHubTransaction.TYPE_HUB_MESSAGE_REQUIRES_RESPONSE,
+                packageName,
+                mNextAvailableMessageSequenceNumber.getAndIncrement(),
+                sessionId) {
+            @Override
+            /* package */ int onTransact() {
+                try {
+                    message.sequenceNumber = getMessageSequenceNumber();
+                    mContextHubProxy.sendMessageToEndpoint(sessionId, message);
+                    return ContextHubTransaction.RESULT_SUCCESS;
+                } catch (RemoteException e) {
+                    Log.e(TAG, "RemoteException while trying to send a session message", e);
+                    return ContextHubTransaction.RESULT_FAILED_UNKNOWN;
+                }
+            }
+
+            @Override
+            /* package */ void onTransactionComplete(@ContextHubTransaction.Result int result) {
+                try {
+                    transactionCallback.onTransactionComplete(result);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "RemoteException while calling client onTransactionComplete", e);
+                }
+            }
+        };
+    }
+
+    /**
      * Creates a transaction for querying for a list of nanoapps.
      *
      * @param contextHubId       the ID of the hub to query
@@ -452,9 +500,14 @@
             mTransactionRecordDeque.add(new TransactionRecord(transaction.toString()));
         }
 
-        if (Flags.reliableMessageRetrySupportService()
-                && transaction.getTransactionType()
-                        == ContextHubTransaction.TYPE_RELIABLE_MESSAGE) {
+        boolean isReliableMessage =
+                Flags.reliableMessageRetrySupportService()
+                        && (transaction.getTransactionType()
+                                == ContextHubTransaction.TYPE_RELIABLE_MESSAGE);
+        boolean isEndpointMessage =
+                (transaction.getTransactionType()
+                        == ContextHubTransaction.TYPE_HUB_MESSAGE_REQUIRES_RESPONSE);
+        if (isReliableMessage || isEndpointMessage) {
             synchronized (mReliableMessageLock) {
                 if (mReliableMessageTransactionMap.size() >= MAX_PENDING_REQUESTS) {
                     throw new IllegalStateException(
@@ -766,10 +819,10 @@
                         mReliableMessageTransactionMap.entrySet().iterator();
                 while (iter.hasNext()) {
                     ContextHubServiceTransaction transaction = iter.next().getValue();
-                    short hostEndpointId = transaction.getHostEndpointId();
+                    long ownerId = transaction.getOwnerId();
                     int numCompletedStartCalls = transaction.getNumCompletedStartCalls();
                     if (numCompletedStartCalls == 0
-                            && mReliableMessageHostEndpointIdActiveSet.contains(hostEndpointId)) {
+                            && mReliableMessageOwnerIdActiveSet.contains(ownerId)) {
                         continue;
                     }
 
@@ -871,7 +924,7 @@
         } else {
             iter.remove();
         }
-        mReliableMessageHostEndpointIdActiveSet.remove(transaction.getHostEndpointId());
+        mReliableMessageOwnerIdActiveSet.remove(transaction.getOwnerId());
     }
 
     /**
@@ -906,7 +959,7 @@
             transaction.setTimeoutTime(now + RELIABLE_MESSAGE_TIMEOUT.toNanos());
         }
         transaction.setNumCompletedStartCalls(numCompletedStartCalls + 1);
-        mReliableMessageHostEndpointIdActiveSet.add(transaction.getHostEndpointId());
+        mReliableMessageOwnerIdActiveSet.add(transaction.getOwnerId());
     }
 
     private int toStatsTransactionResult(@ContextHubTransaction.Result int result) {
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManagerOld.java b/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManagerOld.java
index a67fa30..657375d 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManagerOld.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManagerOld.java
@@ -18,28 +18,14 @@
 
 import android.chre.flags.Flags;
 import android.hardware.location.ContextHubTransaction;
-import android.hardware.location.IContextHubTransactionCallback;
-import android.hardware.location.NanoAppBinary;
-import android.hardware.location.NanoAppMessage;
 import android.hardware.location.NanoAppState;
-import android.os.RemoteException;
 import android.os.SystemClock;
 import android.util.Log;
 
-import java.time.Duration;
-import java.util.ArrayDeque;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
-import java.util.Random;
-import java.util.Set;
-import java.util.concurrent.ScheduledFuture;
-import java.util.concurrent.ScheduledThreadPoolExecutor;
 import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicInteger;
 
 /**
  * Manages transactions at the Context Hub Service.
@@ -326,10 +312,10 @@
                     mReliableMessageTransactionMap.entrySet().iterator();
             while (iter.hasNext()) {
                 ContextHubServiceTransaction transaction = iter.next().getValue();
-                short hostEndpointId = transaction.getHostEndpointId();
+                long ownerId = transaction.getOwnerId();
                 int numCompletedStartCalls = transaction.getNumCompletedStartCalls();
                 if (numCompletedStartCalls == 0
-                        && mReliableMessageHostEndpointIdActiveSet.contains(hostEndpointId)) {
+                        && mReliableMessageOwnerIdActiveSet.contains(ownerId)) {
                     continue;
                 }
 
@@ -394,7 +380,7 @@
         } else {
             iter.remove();
         }
-        mReliableMessageHostEndpointIdActiveSet.remove(transaction.getHostEndpointId());
+        mReliableMessageOwnerIdActiveSet.remove(transaction.getOwnerId());
 
         Log.d(
                 TAG,
@@ -436,7 +422,7 @@
             transaction.setTimeoutTime(now + RELIABLE_MESSAGE_TIMEOUT.toNanos());
         }
         transaction.setNumCompletedStartCalls(numCompletedStartCalls + 1);
-        mReliableMessageHostEndpointIdActiveSet.add(transaction.getHostEndpointId());
+        mReliableMessageOwnerIdActiveSet.add(transaction.getOwnerId());
     }
 
     @Override
diff --git a/services/core/java/com/android/server/location/contexthub/HubInfoRegistry.java b/services/core/java/com/android/server/location/contexthub/HubInfoRegistry.java
index 6f5f191..503f1ac 100644
--- a/services/core/java/com/android/server/location/contexthub/HubInfoRegistry.java
+++ b/services/core/java/com/android/server/location/contexthub/HubInfoRegistry.java
@@ -198,7 +198,8 @@
                 removedInfoList.toArray(new HubEndpointInfo[removedInfoList.size()]),
                 (cb, infoList) -> {
                     try {
-                        cb.onEndpointsStopped(infoList, reason);
+                        cb.onEndpointsStopped(
+                                infoList, ContextHubServiceUtil.toAppHubEndpointReason(reason));
                     } catch (RemoteException e) {
                         if (e instanceof DeadObjectException) {
                             Log.w(TAG, "onEndpointStopped: callback died, unregistering");
diff --git a/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java b/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
index 6cb9429..e1df503 100644
--- a/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
+++ b/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
@@ -21,6 +21,7 @@
 import android.hardware.contexthub.EndpointId;
 import android.hardware.contexthub.HostEndpointInfo;
 import android.hardware.contexthub.HubEndpointInfo;
+import android.hardware.contexthub.Message;
 import android.hardware.contexthub.MessageDeliveryStatus;
 import android.hardware.contexthub.NanSessionRequest;
 import android.hardware.contexthub.V1_0.ContextHub;
@@ -264,6 +265,13 @@
     /** Notifies the completion of a session opened by the HAL */
     public void endpointSessionOpenComplete(int sessionId) throws RemoteException {}
 
+    /** Sends a message to a remote endpoint */
+    public void sendMessageToEndpoint(int sessionId, Message msg) throws RemoteException {}
+
+    /** Sends a message delivery status to a remote endpoint */
+    public void sendMessageDeliveryStatusToEndpoint(int sessionId, MessageDeliveryStatus msgStatus)
+            throws RemoteException {}
+
     /**
      * @return True if this version of the Contexthub HAL supports Location setting notifications.
      */
@@ -757,6 +765,25 @@
             hub.endpointSessionOpenComplete(sessionId);
         }
 
+        @Override
+        public void sendMessageToEndpoint(int sessionId, Message msg) throws RemoteException {
+            android.hardware.contexthub.IContextHub hub = getHub();
+            if (hub == null) {
+                return;
+            }
+            hub.sendMessageToEndpoint(sessionId, msg);
+        }
+
+        @Override
+        public void sendMessageDeliveryStatusToEndpoint(
+                int sessionId, MessageDeliveryStatus msgStatus) throws RemoteException {
+            android.hardware.contexthub.IContextHub hub = getHub();
+            if (hub == null) {
+                return;
+            }
+            hub.sendMessageDeliveryStatusToEndpoint(sessionId, msgStatus);
+        }
+
         public boolean supportsLocationSettingNotifications() {
             return true;
         }
diff --git a/services/core/java/com/android/server/location/fudger/LocationFudger.java b/services/core/java/com/android/server/location/fudger/LocationFudger.java
index 0da1514..bbd8aa1 100644
--- a/services/core/java/com/android/server/location/fudger/LocationFudger.java
+++ b/services/core/java/com/android/server/location/fudger/LocationFudger.java
@@ -16,6 +16,9 @@
 
 package com.android.server.location.fudger;
 
+import static com.android.internal.location.geometry.S2CellIdUtils.LAT_INDEX;
+import static com.android.internal.location.geometry.S2CellIdUtils.LNG_INDEX;
+
 import android.annotation.FlaggedApi;
 import android.annotation.Nullable;
 import android.location.Location;
@@ -184,31 +187,33 @@
         synchronized (this) {
             cacheCopy = mLocationFudgerCache;
         }
-
+        double[] coarsened = new double[] {0.0, 0.0};
         // TODO(b/381204398): To ensure a safe rollout, two algorithms co-exist. The first is the
         // new density-based algorithm, while the second is the traditional coarsening algorithm.
         // Once rollout is done, clean up the unused algorithm.
-        if (Flags.densityBasedCoarseLocations() && cacheCopy != null
-                && cacheCopy.hasDefaultValue()) {
-            int level = cacheCopy.getCoarseningLevel(latitude, longitude);
-            double[] center = snapToCenterOfS2Cell(latitude, longitude, level);
-            latitude = center[S2CellIdUtils.LAT_INDEX];
-            longitude = center[S2CellIdUtils.LNG_INDEX];
+        // The new algorithm is applied if and only if (1) the flag is on, (2) the cache has been
+        // set, and (3) the cache has successfully queried the provider for the default coarsening
+        // value.
+        if (Flags.populationDensityProvider() && Flags.densityBasedCoarseLocations()
+                && cacheCopy != null) {
+            if (cacheCopy.hasDefaultValue()) {
+                // New algorithm that snaps to the center of a S2 cell.
+                int level = cacheCopy.getCoarseningLevel(latitude, longitude);
+                coarsened = snapToCenterOfS2Cell(latitude, longitude, level);
+            } else {
+                // Try to fetch the default value. The answer won't come in time, but will be used
+                // for the next location to coarsen.
+                cacheCopy.fetchDefaultCoarseningLevelIfNeeded();
+                // Previous algorithm that snaps to a grid of width mAccuracyM.
+                coarsened = snapToGrid(latitude, longitude);
+            }
         } else {
-            // quantize location by snapping to a grid. this is the primary means of obfuscation. it
-            // gives nice consistent results and is very effective at hiding the true location (as
-            // long as you are not sitting on a grid boundary, which the random offsets mitigate).
-            //
-            // note that we quantize the latitude first, since the longitude quantization depends on
-            // the latitude value and so leaks information about the latitude
-            double latGranularity = metersToDegreesLatitude(mAccuracyM);
-            latitude = wrapLatitude(Math.round(latitude / latGranularity) * latGranularity);
-            double lonGranularity = metersToDegreesLongitude(mAccuracyM, latitude);
-            longitude = wrapLongitude(Math.round(longitude / lonGranularity) * lonGranularity);
+            // Previous algorithm that snaps to a grid of width mAccuracyM.
+            coarsened = snapToGrid(latitude, longitude);
         }
 
-        coarse.setLatitude(latitude);
-        coarse.setLongitude(longitude);
+        coarse.setLatitude(coarsened[LAT_INDEX]);
+        coarse.setLongitude(coarsened[LNG_INDEX]);
         coarse.setAccuracy(Math.max(mAccuracyM, coarse.getAccuracy()));
 
         synchronized (this) {
@@ -219,6 +224,21 @@
         return coarse;
     }
 
+    // quantize location by snapping to a grid. this is the primary means of obfuscation. it
+    // gives nice consistent results and is very effective at hiding the true location (as
+    // long as you are not sitting on a grid boundary, which the random offsets mitigate).
+    //
+    // note that we quantize the latitude first, since the longitude quantization depends on
+    // the latitude value and so leaks information about the latitude
+    private double[] snapToGrid(double latitude, double longitude) {
+        double[] center = new double[] {0.0, 0.0};
+        double latGranularity = metersToDegreesLatitude(mAccuracyM);
+        center[LAT_INDEX] = wrapLatitude(Math.round(latitude / latGranularity) * latGranularity);
+        double lonGranularity = metersToDegreesLongitude(mAccuracyM, latitude);
+        center[LNG_INDEX] = wrapLongitude(Math.round(longitude / lonGranularity) * lonGranularity);
+        return center;
+    }
+
     @VisibleForTesting
     protected double[] snapToCenterOfS2Cell(double latDegrees, double lngDegrees, int level) {
         long leafCell = S2CellIdUtils.fromLatLngDegrees(latDegrees, lngDegrees);
diff --git a/services/core/java/com/android/server/location/fudger/LocationFudgerCache.java b/services/core/java/com/android/server/location/fudger/LocationFudgerCache.java
index ce8bec8..19ec38c 100644
--- a/services/core/java/com/android/server/location/fudger/LocationFudgerCache.java
+++ b/services/core/java/com/android/server/location/fudger/LocationFudgerCache.java
@@ -76,6 +76,13 @@
         asyncFetchDefaultCoarseningLevel();
     }
 
+    /** If the cache's default coarsening value hasn't been set, asynchronously fetches it. */
+    public void fetchDefaultCoarseningLevelIfNeeded() {
+        if (!hasDefaultValue()) {
+            asyncFetchDefaultCoarseningLevel();
+        }
+    }
+
     /** Returns true if the cache has successfully received a default value from the provider. */
     public boolean hasDefaultValue() {
         synchronized (mLock) {
diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java b/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
index 5ee9452..f09be2c 100644
--- a/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
+++ b/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
@@ -279,13 +279,22 @@
         if (!mRunning) {
             return false;
         }
+        // We bind if any manager is scanning (regardless of whether an app is scanning) to give
+        // the opportunity for providers to publish routing sessions that were established
+        // directly between the app and the provider (typically via AndroidX MediaRouter). See
+        // b/176774510#comment20 for more information.
         boolean bindDueToManagerScan =
                 mIsManagerScanning && !Flags.enablePreventionOfManagerScansWhenNoAppsScan();
-        if (!getSessionInfos().isEmpty() || bindDueToManagerScan) {
-            // We bind if any manager is scanning (regardless of whether an app is scanning) to give
-            // the opportunity for providers to publish routing sessions that were established
-            // directly between the app and the provider (typically via AndroidX MediaRouter). See
-            // b/176774510#comment20 for more information.
+        // We also bind if this provider supports system media routing, because even if an app
+        // doesn't have any registered discovery preference, we should still be able to route their
+        // system media.
+        boolean bindDueToSystemMediaRoutingSupport =
+                mLastDiscoveryPreference != null
+                        && mLastDiscoveryPreference.shouldPerformActiveScan()
+                        && mSupportsSystemMediaRouting;
+        if (!getSessionInfos().isEmpty()
+                || bindDueToManagerScan
+                || bindDueToSystemMediaRoutingSupport) {
             return true;
         }
         boolean anAppIsScanning =
diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderWatcher.java b/services/core/java/com/android/server/media/MediaRoute2ProviderWatcher.java
index 69c460e..42303e0 100644
--- a/services/core/java/com/android/server/media/MediaRoute2ProviderWatcher.java
+++ b/services/core/java/com/android/server/media/MediaRoute2ProviderWatcher.java
@@ -32,6 +32,7 @@
 import android.media.MediaRoute2ProviderService;
 import android.os.Handler;
 import android.os.UserHandle;
+import android.text.TextUtils;
 import android.util.Log;
 import android.util.Slog;
 
@@ -162,8 +163,14 @@
                                     mUserId);
                     Slog.i(
                             TAG,
-                            "Enabling proxy for MediaRoute2ProviderService: "
-                                    + proxy.mComponentName);
+                            TextUtils.formatSimple(
+                                    "Enabling proxy for MediaRoute2ProviderService: %s"
+                                        + " (isSelfScan=%b, supportsSystemMediaRouting=%b,"
+                                        + " userId=%d)",
+                                    proxy.mComponentName,
+                                    isSelfScanOnlyProvider,
+                                    supportsSystemMediaRouting,
+                                    mUserId));
                     proxy.start(/* rebindIfDisconnected= */ false);
                     mProxies.add(targetIndex++, proxy);
                     mCallback.onAddProviderService(proxy);
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index abc067d..58deffc 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -848,7 +848,7 @@
                 UserRecord userRecord = getOrCreateUserRecordLocked(userId);
                 List<RoutingSessionInfo> sessionInfos;
                 if (hasSystemRoutingPermissions) {
-                    if (setDeviceRouteSelected) {
+                    if (setDeviceRouteSelected && !Flags.enableMirroringInMediaRouter2()) {
                         // Return a fake system session that shows the device route as selected and
                         // available bluetooth routes as transferable.
                         return userRecord.mHandler.getSystemProvider()
@@ -2581,9 +2581,9 @@
             mUserRecord = userRecord;
             mSystemProvider =
                     Flags.enableMirroringInMediaRouter2()
-                            ? new SystemMediaRoute2Provider2(
+                            ? SystemMediaRoute2Provider2.create(
                                     service.mContext, UserHandle.of(userRecord.mUserId), looper)
-                            : new SystemMediaRoute2Provider(
+                            : SystemMediaRoute2Provider.create(
                                     service.mContext, UserHandle.of(userRecord.mUserId), looper);
             mRouteProviders.add(getSystemProvider());
             mWatcher = new MediaRoute2ProviderWatcher(service.mContext, this,
@@ -2733,6 +2733,15 @@
                 newRoutes = Collections.emptySet();
             }
 
+            if (Flags.enableMirroringInMediaRouter2()
+                    && provider instanceof MediaRoute2ProviderServiceProxy proxyProvider) {
+                // We notify the system provider of service updates, so that it can update the
+                // system routing session by adding them as transferable routes. And we remove those
+                // that don't support remote routing.
+                mSystemProvider.updateSystemMediaRoutesFromProxy(proxyProvider);
+                newRoutes.removeIf(it -> !it.supportsRemoteRouting());
+            }
+
             // Add new routes to the maps.
             ArrayList<MediaRoute2Info> addedRoutes = new ArrayList<>();
             boolean hasAddedOrModifiedRoutes = false;
diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
index 5fb80ba..b93846b 100644
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
@@ -73,6 +73,10 @@
     // For apps without MODIFYING_AUDIO_ROUTING permission.
     // This should be the currently selected route.
     MediaRoute2Info mDefaultRoute;
+
+    @GuardedBy("mLock")
+    RoutingSessionInfo mSystemSessionInfo;
+
     RoutingSessionInfo mDefaultSessionInfo;
 
     private final AudioManagerBroadcastReceiver mAudioReceiver =
@@ -89,8 +93,12 @@
     @Nullable
     private volatile SessionCreationOrTransferRequest mPendingTransferRequest;
 
-    SystemMediaRoute2Provider(Context context, UserHandle user, Looper looper) {
-        this(context, COMPONENT_NAME, user, looper);
+    public static SystemMediaRoute2Provider create(
+            Context context, UserHandle user, Looper looper) {
+        var instance = new SystemMediaRoute2Provider(context, COMPONENT_NAME, user, looper);
+        instance.updateProviderState();
+        instance.updateSessionInfosIfNeeded();
+        return instance;
     }
 
     protected SystemMediaRoute2Provider(
@@ -124,8 +132,6 @@
                                                 notifySessionInfoUpdated();
                                             }
                                         }));
-        updateProviderState();
-        updateSessionInfosIfNeeded();
     }
 
     public void start() {
@@ -178,7 +184,10 @@
             if (TextUtils.equals(routeOriginalId, mSelectedRouteId)) {
                 RoutingSessionInfo currentSessionInfo;
                 synchronized (mLock) {
-                    currentSessionInfo = mSessionInfos.get(0);
+                    currentSessionInfo =
+                            Flags.enableMirroringInMediaRouter2()
+                                    ? mSystemSessionInfo
+                                    : mSessionInfos.get(0);
                 }
                 mCallback.onSessionCreated(this, requestId, currentSessionInfo);
                 return;
@@ -352,7 +361,10 @@
             }
 
             if (Flags.enableBuiltInSpeakerRouteSuitabilityStatuses()) {
-                RoutingSessionInfo oldSessionInfo = mSessionInfos.get(0);
+                var oldSessionInfo =
+                        Flags.enableMirroringInMediaRouter2()
+                                ? mSystemSessionInfo
+                                : mSessionInfos.get(0);
                 builder.setTransferReason(oldSessionInfo.getTransferReason())
                         .setTransferInitiator(oldSessionInfo.getTransferInitiatorUserHandle(),
                                 oldSessionInfo.getTransferInitiatorPackageName());
@@ -362,7 +374,32 @@
         }
     }
 
-    private void updateProviderState() {
+    /**
+     * Notifies the system provider of a {@link MediaRoute2ProviderServiceProxy} update.
+     *
+     * <p>To be overridden so as to generate system media routes for {@link
+     * MediaRoute2ProviderService} routes that {@link MediaRoute2Info#supportsSystemMediaRouting()
+     * support system media routing}).
+     *
+     * @param serviceProxy The proxy of the service that updated its state.
+     */
+    public void updateSystemMediaRoutesFromProxy(MediaRoute2ProviderServiceProxy serviceProxy) {
+        // Do nothing. This implementation doesn't support MR2ProviderService system media routes.
+        // The subclass overrides this method to implement app-managed system media routing (aka
+        // mirroring).
+    }
+
+    /**
+     * Called when the system provider state changes.
+     *
+     * <p>To be overridden by {@link SystemMediaRoute2Provider2}, so that app-provided system media
+     * routing routes are added before setting the provider state.
+     */
+    public void onSystemProviderRoutesChanged(MediaRoute2ProviderInfo providerInfo) {
+        setProviderState(providerInfo);
+    }
+
+    protected void updateProviderState() {
         MediaRoute2ProviderInfo.Builder builder = new MediaRoute2ProviderInfo.Builder();
 
         // We must have a device route in the provider info.
@@ -371,7 +408,9 @@
             for (MediaRoute2Info route : deviceRoutes) {
                 builder.addRoute(route);
             }
-            setProviderState(builder.build());
+            if (!Flags.enableMirroringInMediaRouter2()) {
+                setProviderState(builder.build());
+            }
         } else {
             builder.addRoute(mDeviceRouteController.getSelectedRoute());
         }
@@ -380,7 +419,7 @@
             builder.addRoute(route);
         }
         MediaRoute2ProviderInfo providerInfo = builder.build();
-        setProviderState(providerInfo);
+        onSystemProviderRoutesChanged(providerInfo);
         if (DEBUG) {
             Slog.d(TAG, "Updating system provider info : " + providerInfo);
         }
@@ -391,8 +430,12 @@
      */
     boolean updateSessionInfosIfNeeded() {
         synchronized (mLock) {
-            RoutingSessionInfo oldSessionInfo = mSessionInfos.isEmpty() ? null : mSessionInfos.get(
-                    0);
+            RoutingSessionInfo oldSessionInfo;
+            if (Flags.enableMirroringInMediaRouter2()) {
+                oldSessionInfo = mSystemSessionInfo;
+            } else {
+                oldSessionInfo = mSessionInfos.isEmpty() ? null : mSessionInfos.get(0);
+            }
 
             RoutingSessionInfo.Builder builder = new RoutingSessionInfo.Builder(
                     SYSTEM_SESSION_ID, "" /* clientPackageName */)
@@ -481,8 +524,8 @@
                 if (DEBUG) {
                     Slog.d(TAG, "Updating system routing session info : " + newSessionInfo);
                 }
-                mSessionInfos.clear();
-                mSessionInfos.add(newSessionInfo);
+                mSystemSessionInfo = newSessionInfo;
+                onSystemSessionInfoUpdated();
                 mDefaultSessionInfo =
                         new RoutingSessionInfo.Builder(
                                         SYSTEM_SESSION_ID, "" /* clientPackageName */)
@@ -499,6 +542,12 @@
         }
     }
 
+    @GuardedBy("mLock")
+    protected void onSystemSessionInfoUpdated() {
+        mSessionInfos.clear();
+        mSessionInfos.add(mSystemSessionInfo);
+    }
+
     @GuardedBy("mRequestLock")
     private void reportPendingSessionRequestResultLockedIfNeeded(
             RoutingSessionInfo newSessionInfo) {
@@ -585,6 +634,9 @@
         RoutingSessionInfo sessionInfo;
         synchronized (mLock) {
             sessionInfo = mSessionInfos.get(0);
+            if (sessionInfo == null) {
+                return;
+            }
         }
 
         mCallback.onSessionUpdated(this, sessionInfo);
diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider2.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider2.java
index 8a14a38..7dc30ab 100644
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider2.java
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider2.java
@@ -16,11 +16,26 @@
 
 package com.android.server.media;
 
+import static android.media.MediaRoute2Info.FEATURE_LIVE_AUDIO;
+
+import android.annotation.Nullable;
 import android.content.ComponentName;
 import android.content.Context;
+import android.media.MediaRoute2Info;
+import android.media.MediaRoute2ProviderInfo;
 import android.media.MediaRoute2ProviderService;
+import android.media.RoutingSessionInfo;
 import android.os.Looper;
 import android.os.UserHandle;
+import android.util.ArraySet;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.stream.Stream;
 
 /**
  * Extends {@link SystemMediaRoute2Provider} by adding system routes provided by {@link
@@ -30,12 +45,142 @@
  */
 /* package */ class SystemMediaRoute2Provider2 extends SystemMediaRoute2Provider {
 
+    private static final String ROUTE_ID_PREFIX_SYSTEM = "SYSTEM";
+    private static final String ROUTE_ID_SYSTEM_SEPARATOR = ".";
+
+    @GuardedBy("mLock")
+    private MediaRoute2ProviderInfo mLastSystemProviderInfo;
+
+    @GuardedBy("mLock")
+    private final Map<String, ProviderProxyRecord> mProxyRecords = new HashMap<>();
+
     private static final ComponentName COMPONENT_NAME =
             new ComponentName(
                     SystemMediaRoute2Provider2.class.getPackage().getName(),
                     SystemMediaRoute2Provider2.class.getName());
 
-    SystemMediaRoute2Provider2(Context context, UserHandle user, Looper looper) {
+    public static SystemMediaRoute2Provider2 create(
+            Context context, UserHandle user, Looper looper) {
+        var instance = new SystemMediaRoute2Provider2(context, user, looper);
+        instance.updateProviderState();
+        instance.updateSessionInfosIfNeeded();
+        return instance;
+    }
+
+    private SystemMediaRoute2Provider2(Context context, UserHandle user, Looper looper) {
         super(context, COMPONENT_NAME, user, looper);
     }
+
+    @Override
+    protected void onSystemSessionInfoUpdated() {
+        updateSessionInfo();
+    }
+
+    @Override
+    public void updateSystemMediaRoutesFromProxy(MediaRoute2ProviderServiceProxy serviceProxy) {
+        var proxyRecord = ProviderProxyRecord.createFor(serviceProxy);
+        synchronized (mLock) {
+            if (proxyRecord == null) {
+                mProxyRecords.remove(serviceProxy.mUniqueId);
+            } else {
+                mProxyRecords.put(serviceProxy.mUniqueId, proxyRecord);
+            }
+            setProviderState(buildProviderInfo());
+        }
+        updateSessionInfo();
+        notifyProviderState();
+        notifySessionInfoUpdated();
+    }
+
+    @Override
+    public void onSystemProviderRoutesChanged(MediaRoute2ProviderInfo providerInfo) {
+        synchronized (mLock) {
+            mLastSystemProviderInfo = providerInfo;
+            setProviderState(buildProviderInfo());
+        }
+        updateSessionInfo();
+        notifySessionInfoUpdated();
+    }
+
+    /**
+     * Updates the {@link #mSessionInfos} by expanding the {@link SystemMediaRoute2Provider} session
+     * with information from the {@link MediaRoute2ProviderService provider services}.
+     */
+    private void updateSessionInfo() {
+        synchronized (mLock) {
+            var systemSessionInfo = mSystemSessionInfo;
+            if (systemSessionInfo == null) {
+                // The system session info hasn't been initialized yet. Do nothing.
+                return;
+            }
+            var builder = new RoutingSessionInfo.Builder(systemSessionInfo);
+            mProxyRecords.values().stream()
+                    .flatMap(ProviderProxyRecord::getRoutesStream)
+                    .map(MediaRoute2Info::getId)
+                    .forEach(builder::addTransferableRoute);
+            mSessionInfos.clear();
+            mSessionInfos.add(builder.build());
+        }
+    }
+
+    /**
+     * Returns a new a provider info that includes all routes from the system provider {@link
+     * SystemMediaRoute2Provider}, along with system routes from {@link MediaRoute2ProviderService
+     * provider services}.
+     */
+    @GuardedBy("mLock")
+    private MediaRoute2ProviderInfo buildProviderInfo() {
+        MediaRoute2ProviderInfo.Builder builder =
+                new MediaRoute2ProviderInfo.Builder(mLastSystemProviderInfo);
+        mProxyRecords.values().stream()
+                .flatMap(ProviderProxyRecord::getRoutesStream)
+                .forEach(builder::addRoute);
+        return builder.build();
+    }
+
+    /**
+     * Holds information about {@link MediaRoute2ProviderService provider services} registered in
+     * the system.
+     *
+     * @param mProxy The corresponding {@link MediaRoute2ProviderServiceProxy}.
+     * @param mSystemMediaRoutes The last snapshot of routes from the service that support system
+     *     media routing, as defined by {@link MediaRoute2Info#supportsSystemMediaRouting()}.
+     */
+    private record ProviderProxyRecord(
+            MediaRoute2ProviderServiceProxy mProxy,
+            Collection<MediaRoute2Info> mSystemMediaRoutes) {
+
+        /** Returns a stream representation of the {@link #mSystemMediaRoutes}. */
+        public Stream<MediaRoute2Info> getRoutesStream() {
+            return mSystemMediaRoutes.stream();
+        }
+
+        /**
+         * Returns a new instance, or null if the given {@code serviceProxy} doesn't have an
+         * associated {@link MediaRoute2ProviderInfo}.
+         */
+        @Nullable
+        public static ProviderProxyRecord createFor(MediaRoute2ProviderServiceProxy serviceProxy) {
+            MediaRoute2ProviderInfo providerInfo = serviceProxy.getProviderInfo();
+            if (providerInfo == null) {
+                return null;
+            }
+            ArraySet<MediaRoute2Info> routes = new ArraySet<>();
+            providerInfo.getRoutes().stream()
+                    .filter(MediaRoute2Info::supportsSystemMediaRouting)
+                    .forEach(
+                            route -> {
+                                String id =
+                                        ROUTE_ID_PREFIX_SYSTEM
+                                                + route.getProviderId()
+                                                + ROUTE_ID_SYSTEM_SEPARATOR
+                                                + route.getOriginalId();
+                                routes.add(
+                                        new MediaRoute2Info.Builder(id, route.getName())
+                                                .addFeature(FEATURE_LIVE_AUDIO)
+                                                .build());
+                            });
+            return new ProviderProxyRecord(serviceProxy, Collections.unmodifiableSet(routes));
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/media/quality/BiMap.java b/services/core/java/com/android/server/media/quality/BiMap.java
new file mode 100644
index 0000000..82b8284
--- /dev/null
+++ b/services/core/java/com/android/server/media/quality/BiMap.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.media.quality;
+
+import android.util.ArrayMap;
+
+import java.util.Collection;
+import java.util.Map;
+
+/**
+ * A very basic bidirectional map.
+ *
+ * @param <K> data type of Key
+ * @param <V> data type of Value
+ */
+public class BiMap<K, V> {
+    private Map<K, V> mPrimaryMap = new ArrayMap<>();
+    private Map<V, K> mSecondaryMap = new ArrayMap<>();
+
+    /**
+     * Add key and associated value to the map
+     *
+     * @param key key to add
+     * @param value value to add
+     * @return true if successfully added, false otherwise
+     */
+    public boolean put(K key, V value) {
+        if (key == null || value == null || mPrimaryMap.containsKey(key)
+                || mSecondaryMap.containsKey(value)) {
+            return false;
+        }
+
+        mPrimaryMap.put(key, value);
+        mSecondaryMap.put(value, key);
+        return true;
+    }
+
+    /**
+     * Remove key and associated value from the map
+     *
+     * @param key key to remove
+     * @return true if removed, false otherwise
+     */
+    public boolean remove(K key) {
+        if (key == null) {
+            return false;
+        }
+        if (mPrimaryMap.containsKey(key)) {
+            V value = getValue(key);
+            mPrimaryMap.remove(key);
+            mSecondaryMap.remove(value);
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Remove value and associated key from the map
+     *
+     * @param value value to remove
+     * @return true if removed, false otherwise
+     */
+    public boolean removeValue(V value) {
+        if (value == null) {
+            return false;
+        }
+        return remove(getKey(value));
+    }
+
+    /**
+     * Get the value
+     *
+     * @param key key for which to get value
+     * @return V
+     */
+    public V getValue(K key) {
+        return mPrimaryMap.get(key);
+    }
+
+    /**
+     * Get the key
+     *
+     * @param value value for which to get key
+     * @return K
+     */
+    public K getKey(V value) {
+        return mSecondaryMap.get(value);
+    }
+
+    /**
+     * Get the values of the map.
+     * @return Collection
+     */
+    public Collection<V> getValues() {
+        return mPrimaryMap.values();
+    }
+
+    /**
+     * Clear the map
+     */
+    public void clear() {
+        mPrimaryMap.clear();
+        mSecondaryMap.clear();
+    }
+}
diff --git a/services/core/java/com/android/server/media/quality/MediaQualityService.java b/services/core/java/com/android/server/media/quality/MediaQualityService.java
index 1673b8e..c810231 100644
--- a/services/core/java/com/android/server/media/quality/MediaQualityService.java
+++ b/services/core/java/com/android/server/media/quality/MediaQualityService.java
@@ -31,19 +31,18 @@
 import android.media.quality.PictureProfileHandle;
 import android.media.quality.SoundProfile;
 import android.media.quality.SoundProfileHandle;
+import android.os.Binder;
 import android.os.PersistableBundle;
 import android.os.UserHandle;
 import android.util.Log;
 
 import com.android.server.SystemService;
 
-import com.google.common.collect.BiMap;
-import com.google.common.collect.HashBiMap;
-
 import org.json.JSONException;
 import org.json.JSONObject;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Locale;
@@ -58,6 +57,7 @@
 
     private static final boolean DEBUG = false;
     private static final String TAG = "MediaQualityService";
+    private static final int MAX_UUID_GENERATION_ATTEMPTS = 10;
     private final Context mContext;
     private final MediaQualityDbHelper mMediaQualityDbHelper;
     private final BiMap<Long, String> mPictureProfileTempIdMap;
@@ -66,8 +66,8 @@
     public MediaQualityService(Context context) {
         super(context);
         mContext = context;
-        mPictureProfileTempIdMap = HashBiMap.create();
-        mSoundProfileTempIdMap = HashBiMap.create();
+        mPictureProfileTempIdMap = new BiMap<>();
+        mSoundProfileTempIdMap = new BiMap<>();
         mMediaQualityDbHelper = new MediaQualityDbHelper(mContext);
         mMediaQualityDbHelper.setWriteAheadLoggingEnabled(true);
         mMediaQualityDbHelper.setIdleConnectionTimeout(30);
@@ -85,29 +85,40 @@
         public PictureProfile createPictureProfile(PictureProfile pp, UserHandle user) {
             SQLiteDatabase db = mMediaQualityDbHelper.getWritableDatabase();
 
-            ContentValues values = new ContentValues();
-            values.put(BaseParameters.PARAMETER_TYPE, pp.getProfileType());
-            values.put(BaseParameters.PARAMETER_NAME, pp.getName());
-            values.put(BaseParameters.PARAMETER_PACKAGE, pp.getPackageName());
-            values.put(BaseParameters.PARAMETER_INPUT_ID, pp.getInputId());
-            values.put(mMediaQualityDbHelper.SETTINGS, persistableBundleToJson(pp.getParameters()));
+            ContentValues values = getContentValues(null,
+                    pp.getProfileType(),
+                    pp.getName(),
+                    pp.getPackageName(),
+                    pp.getInputId(),
+                    pp.getParameters());
 
             // id is auto-generated by SQLite upon successful insertion of row
             Long id = db.insert(mMediaQualityDbHelper.PICTURE_QUALITY_TABLE_NAME,
                     null, values);
             populateTempIdMap(mPictureProfileTempIdMap, id);
-            pp.setProfileId(mPictureProfileTempIdMap.get(id));
+            pp.setProfileId(mPictureProfileTempIdMap.getValue(id));
             return pp;
         }
 
         @Override
         public void updatePictureProfile(String id, PictureProfile pp, UserHandle user) {
-            // TODO: implement
+            Long intId = mPictureProfileTempIdMap.getKey(id);
+
+            ContentValues values = getContentValues(intId,
+                    pp.getProfileType(),
+                    pp.getName(),
+                    pp.getPackageName(),
+                    pp.getInputId(),
+                    pp.getParameters());
+
+            SQLiteDatabase db = mMediaQualityDbHelper.getWritableDatabase();
+            db.replace(mMediaQualityDbHelper.PICTURE_QUALITY_TABLE_NAME,
+                    null, values);
         }
 
         @Override
         public void removePictureProfile(String id, UserHandle user) {
-            Long intId = mPictureProfileTempIdMap.inverse().get(id);
+            Long intId = mPictureProfileTempIdMap.getKey(id);
             if (intId != null) {
                 SQLiteDatabase db = mMediaQualityDbHelper.getWritableDatabase();
                 String selection = BaseParameters.PARAMETER_ID + " = ?";
@@ -128,7 +139,7 @@
             try (
                     Cursor cursor = getCursorAfterQuerying(
                             mMediaQualityDbHelper.PICTURE_QUALITY_TABLE_NAME,
-                            getAllMediaProfileColumns(), selection, selectionArguments)
+                            getMediaProfileColumns(includeParams), selection, selectionArguments)
             ) {
                 int count = cursor.getCount();
                 if (count == 0) {
@@ -150,13 +161,18 @@
                 String packageName, boolean includeParams, UserHandle user) {
             String selection = BaseParameters.PARAMETER_PACKAGE + " = ?";
             String[] selectionArguments = {packageName};
-            return getPictureProfilesBasedOnConditions(getAllMediaProfileColumns(), selection,
-                    selectionArguments);
+            return getPictureProfilesBasedOnConditions(getMediaProfileColumns(includeParams),
+                    selection, selectionArguments);
         }
 
         @Override
         public List<PictureProfile> getAvailablePictureProfiles(
                 boolean includeParams, UserHandle user) {
+            String[] packageNames = mContext.getPackageManager().getPackagesForUid(
+                    Binder.getCallingUid());
+            if (packageNames != null && packageNames.length == 1 && !packageNames[0].isEmpty()) {
+                return getPictureProfilesByPackage(packageNames[0], includeParams, user);
+            }
             return new ArrayList<>();
         }
 
@@ -191,29 +207,39 @@
         public SoundProfile createSoundProfile(SoundProfile sp, UserHandle user) {
             SQLiteDatabase db = mMediaQualityDbHelper.getWritableDatabase();
 
-            ContentValues values = new ContentValues();
-            values.put(BaseParameters.PARAMETER_TYPE, sp.getProfileType());
-            values.put(BaseParameters.PARAMETER_NAME, sp.getName());
-            values.put(BaseParameters.PARAMETER_PACKAGE, sp.getPackageName());
-            values.put(BaseParameters.PARAMETER_INPUT_ID, sp.getInputId());
-            values.put(mMediaQualityDbHelper.SETTINGS, persistableBundleToJson(sp.getParameters()));
+            ContentValues values = getContentValues(null,
+                    sp.getProfileType(),
+                    sp.getName(),
+                    sp.getPackageName(),
+                    sp.getInputId(),
+                    sp.getParameters());
 
             // id is auto-generated by SQLite upon successful insertion of row
             Long id = db.insert(mMediaQualityDbHelper.SOUND_QUALITY_TABLE_NAME,
                     null, values);
             populateTempIdMap(mSoundProfileTempIdMap, id);
-            sp.setProfileId(mSoundProfileTempIdMap.get(id));
+            sp.setProfileId(mSoundProfileTempIdMap.getValue(id));
             return sp;
         }
 
         @Override
-        public void updateSoundProfile(String id, SoundProfile pp, UserHandle user) {
-            // TODO: implement
+        public void updateSoundProfile(String id, SoundProfile sp, UserHandle user) {
+            Long intId = mSoundProfileTempIdMap.getKey(id);
+
+            ContentValues values = getContentValues(intId,
+                    sp.getProfileType(),
+                    sp.getName(),
+                    sp.getPackageName(),
+                    sp.getInputId(),
+                    sp.getParameters());
+
+            SQLiteDatabase db = mMediaQualityDbHelper.getWritableDatabase();
+            db.replace(mMediaQualityDbHelper.SOUND_QUALITY_TABLE_NAME, null, values);
         }
 
         @Override
         public void removeSoundProfile(String id, UserHandle user) {
-            Long intId = mSoundProfileTempIdMap.inverse().get(id);
+            Long intId = mSoundProfileTempIdMap.getKey(id);
             if (intId != null) {
                 SQLiteDatabase db = mMediaQualityDbHelper.getWritableDatabase();
                 String selection = BaseParameters.PARAMETER_ID + " = ?";
@@ -234,7 +260,7 @@
             try (
                     Cursor cursor = getCursorAfterQuerying(
                             mMediaQualityDbHelper.SOUND_QUALITY_TABLE_NAME,
-                            getAllMediaProfileColumns(), selection, selectionArguments)
+                            getMediaProfileColumns(includeParams), selection, selectionArguments)
             ) {
                 int count = cursor.getCount();
                 if (count == 0) {
@@ -256,13 +282,18 @@
                 String packageName, boolean includeParams, UserHandle user) {
             String selection = BaseParameters.PARAMETER_PACKAGE + " = ?";
             String[] selectionArguments = {packageName};
-            return getSoundProfilesBasedOnConditions(getAllMediaProfileColumns(), selection,
-                    selectionArguments);
+            return getSoundProfilesBasedOnConditions(getMediaProfileColumns(includeParams),
+                    selection, selectionArguments);
         }
 
         @Override
         public List<SoundProfile> getAvailableSoundProfiles(
                 boolean includeParams, UserHandle user) {
+            String[] packageNames = mContext.getPackageManager().getPackagesForUid(
+                    Binder.getCallingUid());
+            if (packageNames != null && packageNames.length == 1 && !packageNames[0].isEmpty()) {
+                return getSoundProfilesByPackage(packageNames[0], includeParams, user);
+            }
             return new ArrayList<>();
         }
 
@@ -284,12 +315,17 @@
         }
 
         private void populateTempIdMap(BiMap<Long, String> map, Long id) {
-            if (id != null && map.get(id) == null) {
-                String uuid = UUID.randomUUID().toString();
-                while (map.inverse().containsKey(uuid)) {
+            if (id != null && map.getValue(id) == null) {
+                String uuid;
+                int attempts = 0;
+                while (attempts < MAX_UUID_GENERATION_ATTEMPTS) {
                     uuid = UUID.randomUUID().toString();
+                    if (map.getKey(uuid) == null) {
+                        map.put(id, uuid);
+                        return;
+                    }
+                    attempts++;
                 }
-                map.put(id, uuid);
             }
         }
 
@@ -316,7 +352,7 @@
             return json.toString();
         }
 
-        private PersistableBundle jsonToBundle(String jsonString) {
+        private PersistableBundle jsonToPersistableBundle(String jsonString) {
             PersistableBundle bundle = new PersistableBundle();
             if (jsonString != null) {
                 JSONObject jsonObject = null;
@@ -347,15 +383,42 @@
             return bundle;
         }
 
-        private String[] getAllMediaProfileColumns() {
-            return new String[]{
+        private ContentValues getContentValues(Long dbId, Integer profileType, String name,
+                String packageName, String inputId, PersistableBundle params) {
+            ContentValues values = new ContentValues();
+            if (dbId != null) {
+                values.put(BaseParameters.PARAMETER_ID, dbId);
+            }
+            if (profileType != null) {
+                values.put(BaseParameters.PARAMETER_TYPE, profileType);
+            }
+            if (name != null) {
+                values.put(BaseParameters.PARAMETER_NAME, name);
+            }
+            if (packageName != null) {
+                values.put(BaseParameters.PARAMETER_PACKAGE, packageName);
+            }
+            if (inputId != null) {
+                values.put(BaseParameters.PARAMETER_INPUT_ID, inputId);
+            }
+            if (params != null) {
+                values.put(mMediaQualityDbHelper.SETTINGS, persistableBundleToJson(params));
+            }
+            return values;
+        }
+
+        private String[] getMediaProfileColumns(boolean includeParams) {
+            ArrayList<String> columns = new ArrayList<>(Arrays.asList(
                     BaseParameters.PARAMETER_ID,
                     BaseParameters.PARAMETER_TYPE,
                     BaseParameters.PARAMETER_NAME,
                     BaseParameters.PARAMETER_INPUT_ID,
-                    BaseParameters.PARAMETER_PACKAGE,
-                    mMediaQualityDbHelper.SETTINGS
-            };
+                    BaseParameters.PARAMETER_PACKAGE)
+            );
+            if (includeParams) {
+                columns.add(mMediaQualityDbHelper.SETTINGS);
+            }
+            return columns.toArray(new String[0]);
         }
 
         private PictureProfile getPictureProfileWithTempIdFromCursor(Cursor cursor) {
@@ -365,7 +428,7 @@
                     getName(cursor),
                     getInputId(cursor),
                     getPackageName(cursor),
-                    jsonToBundle(getSettingsString(cursor)),
+                    jsonToPersistableBundle(getSettingsString(cursor)),
                     PictureProfileHandle.NONE
             );
         }
@@ -377,7 +440,7 @@
                     getName(cursor),
                     getInputId(cursor),
                     getPackageName(cursor),
-                    jsonToBundle(getSettingsString(cursor)),
+                    jsonToPersistableBundle(getSettingsString(cursor)),
                     SoundProfileHandle.NONE
             );
         }
@@ -386,7 +449,7 @@
             int colIndex = cursor.getColumnIndex(BaseParameters.PARAMETER_ID);
             Long dbId = colIndex != -1 ? cursor.getLong(colIndex) : null;
             populateTempIdMap(map, dbId);
-            return map.get(dbId);
+            return map.getValue(dbId);
         }
 
         private int getType(Cursor cursor) {
diff --git a/services/core/java/com/android/server/media/quality/OWNERS b/services/core/java/com/android/server/media/quality/OWNERS
index e455846..7171aa4 100644
--- a/services/core/java/com/android/server/media/quality/OWNERS
+++ b/services/core/java/com/android/server/media/quality/OWNERS
@@ -1,2 +1,3 @@
 shubang@google.com
-haofanw@google.com
\ No newline at end of file
+haofanw@google.com
+pkandhalu@google.com
\ No newline at end of file
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 39eea74..c6d7fc7 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -361,6 +361,7 @@
 import com.android.server.notification.GroupHelper.NotificationAttributes;
 import com.android.server.notification.ManagedServices.ManagedServiceInfo;
 import com.android.server.notification.ManagedServices.UserProfiles;
+import com.android.server.notification.NotificationRecordLogger.NotificationReportedEvent;
 import com.android.server.notification.toast.CustomToastRecord;
 import com.android.server.notification.toast.TextToastRecord;
 import com.android.server.notification.toast.ToastRecord;
@@ -1934,10 +1935,20 @@
                 hasSensitiveContent, lifespanMs);
     }
 
-    protected void logClassificationChannelAdjustmentReceived(boolean hasPosted, boolean isAlerting,
-                                                              int classification, int lifespanMs) {
+    protected void logClassificationChannelAdjustmentReceived(NotificationRecord r,
+                                                              boolean hasPosted,
+                                                              int classification) {
+        // Note that this value of isAlerting does not fully indicate whether a notif
+        // would make a sound or HUN on device; it is an approximation for metrics.
+        boolean isAlerting = r.getChannel().getImportance() >= IMPORTANCE_DEFAULT;
+        int instanceId = r.getSbn().getInstanceId() == null
+                ? 0 : r.getSbn().getInstanceId().getId();
+
         FrameworkStatsLog.write(FrameworkStatsLog.NOTIFICATION_CHANNEL_CLASSIFICATION,
-                hasPosted, isAlerting, classification, lifespanMs);
+                hasPosted, isAlerting, classification,
+                r.getLifespanMs(System.currentTimeMillis()),
+                NotificationReportedEvent.NOTIFICATION_ADJUSTED.getId(),
+                instanceId, r.getUid());
     }
 
     protected final BroadcastReceiver mLocaleChangeReceiver = new BroadcastReceiver() {
@@ -6134,7 +6145,7 @@
         }
 
         private void enforcePolicyAccess(int uid, String method) {
-            if (PERMISSION_GRANTED == getContext().checkCallingPermission(
+            if (PERMISSION_GRANTED == getContext().checkCallingOrSelfPermission(
                     android.Manifest.permission.MANAGE_NOTIFICATIONS)) {
                 return;
             }
@@ -6165,7 +6176,7 @@
         }
 
         private void enforcePolicyAccess(String pkg, String method) {
-            if (PERMISSION_GRANTED == getContext().checkCallingPermission(
+            if (PERMISSION_GRANTED == getContext().checkCallingOrSelfPermission(
                     android.Manifest.permission.MANAGE_NOTIFICATIONS)) {
                 return;
             }
@@ -6974,6 +6985,7 @@
 
     protected void checkNotificationListenerAccess() {
         if (!isCallerSystemOrPhone()) {
+            // Safe to check calling permission as caller is already not system or phone
             getContext().enforceCallingPermission(
                     permission.MANAGE_NOTIFICATION_LISTENERS,
                     "Caller must hold " + permission.MANAGE_NOTIFICATION_LISTENERS);
@@ -7052,11 +7064,8 @@
                     int classification = adjustments.getInt(KEY_TYPE);
                     // swap app provided type with the real thing
                     adjustments.putParcelable(KEY_TYPE, newChannel);
-                    // Note that this value of isAlerting does not fully indicate whether a notif
-                    // would make a sound or HUN on device; it is an approximation for metrics.
-                    boolean isAlerting = r.getChannel().getImportance() >= IMPORTANCE_DEFAULT;
-                    logClassificationChannelAdjustmentReceived(isPosted, isAlerting, classification,
-                            r.getLifespanMs(System.currentTimeMillis()));
+
+                    logClassificationChannelAdjustmentReceived(r, isPosted, classification);
                 }
             }
             r.addAdjustment(adjustment);
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 95aff56..b571d62 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -1554,15 +1554,18 @@
 
         if (isFromApp) {
             // Don't allow apps to toggle hidden (non-public-API) effects.
-            newEffects = new ZenDeviceEffects.Builder(newEffects)
-                    .setShouldDisableAutoBrightness(oldEffects.shouldDisableAutoBrightness())
-                    .setShouldDisableTapToWake(oldEffects.shouldDisableTapToWake())
-                    .setShouldDisableTiltToWake(oldEffects.shouldDisableTiltToWake())
-                    .setShouldDisableTouch(oldEffects.shouldDisableTouch())
-                    .setShouldMinimizeRadioUsage(oldEffects.shouldMinimizeRadioUsage())
-                    .setShouldMaximizeDoze(oldEffects.shouldMaximizeDoze())
-                    .setExtraEffects(oldEffects.getExtraEffects())
-                    .build();
+            newEffects =
+                    new ZenDeviceEffects.Builder(newEffects)
+                            .setShouldDisableAutoBrightness(
+                                    oldEffects.shouldDisableAutoBrightness())
+                            .setShouldDisableTapToWake(oldEffects.shouldDisableTapToWake())
+                            .setShouldDisableTiltToWake(oldEffects.shouldDisableTiltToWake())
+                            .setShouldDisableTouch(oldEffects.shouldDisableTouch())
+                            .setShouldMinimizeRadioUsage(oldEffects.shouldMinimizeRadioUsage())
+                            .setShouldMaximizeDoze(oldEffects.shouldMaximizeDoze())
+                            .setShouldUseNightLight(oldEffects.shouldUseNightLight())
+                            .setExtraEffects(oldEffects.getExtraEffects())
+                            .build();
         }
 
         zenRule.zenDeviceEffects = newEffects;
@@ -1601,6 +1604,9 @@
             if (oldEffects.shouldMaximizeDoze() != newEffects.shouldMaximizeDoze()) {
                 userModifiedFields |= ZenDeviceEffects.FIELD_MAXIMIZE_DOZE;
             }
+            if (oldEffects.shouldUseNightLight() != newEffects.shouldUseNightLight()) {
+                userModifiedFields |= ZenDeviceEffects.FIELD_NIGHT_LIGHT;
+            }
             if (!Objects.equals(oldEffects.getExtraEffects(), newEffects.getExtraEffects())) {
                 userModifiedFields |= ZenDeviceEffects.FIELD_EXTRA_EFFECTS;
             }
diff --git a/services/core/java/com/android/server/pm/BroadcastHelper.java b/services/core/java/com/android/server/pm/BroadcastHelper.java
index 67e1095..676c6fa 100644
--- a/services/core/java/com/android/server/pm/BroadcastHelper.java
+++ b/services/core/java/com/android/server/pm/BroadcastHelper.java
@@ -365,8 +365,8 @@
         if (isForWholeApp || !android.content.pm.Flags.reduceBroadcastsForComponentStateChanges()) {
             tracePackageChangedBroadcastEvent(
                     android.content.pm.Flags.reduceBroadcastsForComponentStateChanges(),
-                    reasonForTrace, "all" /* targetName */, "whole" /* targetComponent */,
-                    componentNames.size());
+                    reasonForTrace, packageName, "<implicit>" /* targetPackageName */,
+                    "whole" /* targetComponent */, componentNames.size());
             sendPackageChangedBroadcastWithPermissions(packageName, dontKillApp, componentNames,
                     packageUid, reason, userIds, instantUserIds, broadcastAllowList,
                     null /* targetPackageName */, null /* requiredPermissions */);
@@ -388,8 +388,8 @@
 
             // First, send the PACKAGE_CHANGED broadcast to the system.
             if (!TextUtils.equals(packageName, "android")) {
-                tracePackageChangedBroadcastEvent(true /* applyFlag */, reasonForTrace,
-                        "system" /* targetName */, "notExported" /* targetComponent */,
+                tracePackageChangedBroadcastEvent(true /* applyFlag */, reasonForTrace, packageName,
+                        "android" /* targetPackageName */, "notExported" /* targetComponent */,
                         notExportedComponentNames.size());
                 sendPackageChangedBroadcastWithPermissions(packageName, dontKillApp,
                         notExportedComponentNames, packageUid, reason, userIds, instantUserIds,
@@ -399,8 +399,8 @@
             }
 
             // Second, send the PACKAGE_CHANGED broadcast to the application itself.
-            tracePackageChangedBroadcastEvent(true /* applyFlag */, reasonForTrace,
-                    "applicationItself" /* targetName */, "notExported" /* targetComponent */,
+            tracePackageChangedBroadcastEvent(true /* applyFlag */, reasonForTrace, packageName,
+                    packageName /* targetPackageName */, "notExported" /* targetComponent */,
                     notExportedComponentNames.size());
             sendPackageChangedBroadcastWithPermissions(packageName, dontKillApp,
                     notExportedComponentNames, packageUid, reason, userIds, instantUserIds,
@@ -413,8 +413,8 @@
                 if (TextUtils.equals(packageName, sharedPackage)) {
                     continue;
                 }
-                tracePackageChangedBroadcastEvent(true /* applyFlag */, reasonForTrace,
-                        "sharedUidPackages" /* targetName */, "notExported" /* targetComponent */,
+                tracePackageChangedBroadcastEvent(true /* applyFlag */, reasonForTrace, packageName,
+                        sharedPackage /* targetPackageName */, "notExported" /* targetComponent */,
                         notExportedComponentNames.size());
                 sendPackageChangedBroadcastWithPermissions(packageName, dontKillApp,
                         notExportedComponentNames, packageUid, reason, userIds, instantUserIds,
@@ -425,8 +425,8 @@
         }
 
         if (!exportedComponentNames.isEmpty()) {
-            tracePackageChangedBroadcastEvent(true /* applyFlag */, reasonForTrace,
-                    "all" /* targetName */, "exported" /* targetComponent */,
+            tracePackageChangedBroadcastEvent(true /* applyFlag */, reasonForTrace, packageName,
+                    "<implicit>" /* targetPackageName */, "exported" /* targetComponent */,
                     exportedComponentNames.size());
             sendPackageChangedBroadcastWithPermissions(packageName, dontKillApp,
                     exportedComponentNames, packageUid, reason, userIds, instantUserIds,
@@ -1274,8 +1274,10 @@
                 uids, mHandler);
     }
 
-    private static void tracePackageChangedBroadcastEvent(boolean applyFlag, String reasonForTrace,
-            String targetName, String targetComponent, int componentSize) {
+    private static void tracePackageChangedBroadcastEvent(boolean applyFlag,
+            @NonNull String reasonForTrace, @Nullable String packageName,
+            @Nullable String targetPackageName, @Nullable String targetComponent,
+            int componentSize) {
 
         if (!Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
             return;
@@ -1285,7 +1287,8 @@
         builder.append("broadcastPackageChanged; ");
         builder.append("af="); builder.append(applyFlag);
         builder.append(",rft="); builder.append(reasonForTrace);
-        builder.append(",tn="); builder.append(targetName);
+        builder.append(",pn="); builder.append(packageName);
+        builder.append(",tpn="); builder.append(targetPackageName);
         builder.append(",tc="); builder.append(targetComponent);
         builder.append(",cs="); builder.append(componentSize);
 
diff --git a/services/core/java/com/android/server/pm/InstallDependencyHelper.java b/services/core/java/com/android/server/pm/InstallDependencyHelper.java
index 837adf0..81bb929 100644
--- a/services/core/java/com/android/server/pm/InstallDependencyHelper.java
+++ b/services/core/java/com/android/server/pm/InstallDependencyHelper.java
@@ -16,6 +16,7 @@
 
 package com.android.server.pm;
 
+import static android.app.role.RoleManager.ROLE_SYSTEM_DEPENDENCY_INSTALLER;
 import static android.content.pm.PackageInstaller.ACTION_INSTALL_DEPENDENCY;
 import static android.content.pm.PackageManager.INSTALL_FAILED_MISSING_SHARED_LIBRARY;
 import static android.os.Process.SYSTEM_UID;
@@ -56,8 +57,6 @@
 public class InstallDependencyHelper {
     private static final String TAG = InstallDependencyHelper.class.getSimpleName();
     private static final boolean DEBUG = true;
-    private static final String ROLE_SYSTEM_DEPENDENCY_INSTALLER =
-            "android.app.role.SYSTEM_DEPENDENCY_INSTALLER";
     // The maximum amount of time to wait before the system unbinds from the verifier.
     private static final long UNBIND_TIMEOUT_MILLIS = TimeUnit.HOURS.toMillis(6);
     private static final long REQUEST_TIMEOUT_MILLIS = TimeUnit.MINUTES.toMillis(1);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index aadf112..a0bbc45 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -3020,6 +3020,16 @@
         mDexOptHelper.performPackageDexOptUpgradeIfNeeded();
     }
 
+    public void updateMetricsIfNeeded() {
+        final DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
+        if (displayManager != null) {
+            final Display display = displayManager.getDisplay(Display.DEFAULT_DISPLAY);
+            if (display != null) {
+                display.getMetrics(mMetrics);
+            }
+        }
+    }
+
     private void notifyPackageUseInternal(String packageName, int reason) {
         long time = System.currentTimeMillis();
         synchronized (mLock) {
diff --git a/services/core/java/com/android/server/pm/RestrictionsSet.java b/services/core/java/com/android/server/pm/RestrictionsSet.java
index 0804769..38075c1 100644
--- a/services/core/java/com/android/server/pm/RestrictionsSet.java
+++ b/services/core/java/com/android/server/pm/RestrictionsSet.java
@@ -65,6 +65,7 @@
             throw new IllegalArgumentException("empty restriction bundle cannot be added.");
         }
         mUserRestrictions.put(userId, restrictions);
+        UserManager.invalidateUserRestriction();
     }
 
     /**
@@ -84,6 +85,7 @@
         } else {
             mUserRestrictions.delete(userId);
         }
+        UserManager.invalidateUserRestriction();
         return true;
     }
 
@@ -102,6 +104,9 @@
                 removed = true;
             }
         }
+        if (removed) {
+            UserManager.invalidateUserRestriction();
+        }
         return removed;
     }
 
@@ -129,6 +134,7 @@
                     i--;
                 }
             }
+            UserManager.invalidateUserRestriction();
         }
     }
 
@@ -192,6 +198,7 @@
     public boolean remove(@UserIdInt int userId) {
         boolean hasUserRestriction = mUserRestrictions.contains(userId);
         mUserRestrictions.remove(userId);
+        UserManager.invalidateUserRestriction();
         return hasUserRestriction;
     }
 
@@ -200,6 +207,7 @@
      */
     public void removeAllRestrictions() {
         mUserRestrictions.clear();
+        UserManager.invalidateUserRestriction();
     }
 
     /**
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 066fce0..8249d65 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -1113,6 +1113,7 @@
                 UserManager.invalidateUserPropertiesCache();
             }
             UserManager.invalidateCacheOnUserListChange();
+            UserManager.invalidateUserRestriction();
         }
     }
 
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 09feb18..6ab3059 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -216,12 +216,12 @@
 
     private static final Set<String> SENSORS_PERMISSIONS = new ArraySet<>();
     static {
+        SENSORS_PERMISSIONS.add(Manifest.permission.BODY_SENSORS);
+        SENSORS_PERMISSIONS.add(Manifest.permission.BODY_SENSORS_BACKGROUND);
+
         if (Flags.replaceBodySensorPermissionEnabled()) {
             SENSORS_PERMISSIONS.add(HealthPermissions.READ_HEART_RATE);
             SENSORS_PERMISSIONS.add(HealthPermissions.READ_HEALTH_DATA_IN_BACKGROUND);
-        } else {
-            SENSORS_PERMISSIONS.add(Manifest.permission.BODY_SENSORS);
-            SENSORS_PERMISSIONS.add(Manifest.permission.BODY_SENSORS_BACKGROUND);
         }
     }
 
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 05bc69a..672eb4c 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -264,6 +264,16 @@
                 persistentDeviceId, mPermissionManagerServiceImpl::checkUidPermission);
     }
 
+    @Override
+    @Context.PermissionRequestState
+    public int getPermissionRequestState(@NonNull String packageName,
+            @NonNull String permissionName, int deviceId) {
+        Objects.requireNonNull(permissionName, "permission can't be null.");
+        Objects.requireNonNull(packageName, "package name can't be null.");
+        return mPermissionManagerServiceImpl.getPermissionRequestState(packageName, permissionName,
+                getPersistentDeviceId(deviceId));
+    }
+
     private String getPersistentDeviceId(int deviceId) {
         if (deviceId == Context.DEVICE_ID_DEFAULT) {
             return VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT;
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
index ea71953..ca70bddc 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
@@ -1014,6 +1014,11 @@
     }
 
     @Override
+    public int getPermissionRequestState(String packageName, String permName, String deviceId) {
+        throw new IllegalStateException("getPermissionRequestState is not supported.");
+    }
+
+    @Override
     public Map<String, PermissionManager.PermissionState> getAllPermissionStates(
             @NonNull String packageName, @NonNull String deviceId, int userId) {
         throw new UnsupportedOperationException(
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java
index 754b141..b607832 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java
@@ -20,6 +20,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
+import android.content.Context;
 import android.content.pm.PackageManager;
 import android.content.pm.PermissionGroupInfo;
 import android.content.pm.PermissionInfo;
@@ -407,6 +408,16 @@
     int checkUidPermission(int uid, String permName, String deviceId);
 
     /**
+     * Returns one of the permission state
+     * {@link Context.PermissionRequestState#PERMISSION_REQUEST_STATE_GRANTED},
+     * {@link Context.PermissionRequestState#PERMISSION_REQUEST_STATE_REQUESTABLE}, or
+     * {@link Context.PermissionRequestState#PERMISSION_REQUEST_STATE_UNREQUESTABLE}
+     *  for permission request permission flow.
+     */
+    int getPermissionRequestState(@NonNull String packageName, @NonNull String permName,
+            @NonNull String deviceId);
+
+    /**
      * Gets the permission states for requested package, persistent device and user.
      * <p>
      * <strong>Note: </strong>Default device permissions are not inherited in this API. Returns the
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceLoggingDecorator.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceLoggingDecorator.java
index c18f856..ba5e97e 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceLoggingDecorator.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceLoggingDecorator.java
@@ -247,6 +247,13 @@
     }
 
     @Override
+    public int getPermissionRequestState(String packageName, String permName, String deviceId) {
+        Log.i(LOG_TAG, "checkUidPermissionState(permName = " + permName + ", deviceId = "
+                + deviceId + ", packageName = " + packageName + ")");
+        return mService.getPermissionRequestState(packageName, permName, deviceId);
+    }
+
+    @Override
     public Map<String, PermissionState> getAllPermissionStates(@NonNull String packageName,
             @NonNull String deviceId, int userId) {
         Log.i(LOG_TAG,
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTestingShim.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTestingShim.java
index 40139ba..008c14d 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTestingShim.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTestingShim.java
@@ -319,6 +319,11 @@
     }
 
     @Override
+    public int getPermissionRequestState(String packageName, String permName, String deviceId) {
+        return mNewImplementation.getPermissionRequestState(packageName, permName, deviceId);
+    }
+
+    @Override
     public Map<String, PermissionState> getAllPermissionStates(@NonNull String packageName,
             @NonNull String deviceId, int userId) {
         return mNewImplementation.getAllPermissionStates(packageName, deviceId, userId);
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTracingDecorator.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTracingDecorator.java
index 981d3d9..2a47f51 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTracingDecorator.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTracingDecorator.java
@@ -345,6 +345,18 @@
         }
     }
 
+
+    @Override
+    public int getPermissionRequestState(String packageName, String permName, String deviceId) {
+        Trace.traceBegin(TRACE_TAG,
+                "TaggedTracingPermissionManagerServiceImpl#checkUidPermissionState");
+        try {
+            return mService.getPermissionRequestState(packageName, permName, deviceId);
+        } finally {
+            Trace.traceEnd(TRACE_TAG);
+        }
+    }
+
     @Override
     public Map<String, PermissionState> getAllPermissionStates(@NonNull String packageName,
             @NonNull String deviceId, int userId) {
diff --git a/services/core/java/com/android/server/policy/AppOpsPolicy.java b/services/core/java/com/android/server/policy/AppOpsPolicy.java
index ecffd38..3f9144f 100644
--- a/services/core/java/com/android/server/policy/AppOpsPolicy.java
+++ b/services/core/java/com/android/server/policy/AppOpsPolicy.java
@@ -136,7 +136,8 @@
 
         final LocationManagerInternal locationManagerInternal = LocalServices.getService(
                 LocationManagerInternal.class);
-        locationManagerInternal.setLocationPackageTagsListener(
+        if (locationManagerInternal != null) {
+            locationManagerInternal.setLocationPackageTagsListener(
                 (uid, packageTagsList) -> {
                     synchronized (mLock) {
                         if (packageTagsList.isEmpty()) {
@@ -158,6 +159,7 @@
                                 mLocationTags);
                     }
                 });
+        }
 
         final IntentFilter intentFilter = new IntentFilter();
         intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index f1a4811..c4d1cc7 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -3576,7 +3576,7 @@
                 }
                 break;
             case KeyEvent.KEYCODE_I:
-                if (firstDown && event.isMetaPressed()) {
+                if (firstDown && event.isMetaPressed() && isUserSetupComplete() && !keyguardOn) {
                     showSystemSettings();
                     notifyKeyGestureCompleted(event,
                             KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS);
@@ -4106,6 +4106,7 @@
                     case KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT:
                     case KeyGestureEvent.KEY_GESTURE_TYPE_CLOSE_ALL_DIALOGS:
                     case KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION:
+                    case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_DO_NOT_DISTURB:
                         return true;
                     case KeyGestureEvent.KEY_GESTURE_TYPE_SCREENSHOT_CHORD:
                     case KeyGestureEvent.KEY_GESTURE_TYPE_RINGER_TOGGLE_CHORD:
@@ -4120,18 +4121,6 @@
                                 .isAccessibilityShortcutAvailable(false);
                     case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_TALKBACK:
                         return enableTalkbackAndMagnifierKeyGestures();
-                    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;
                 }
@@ -4149,6 +4138,7 @@
         int displayId = event.getDisplayId();
         int modifierState = event.getModifierState();
         boolean keyguardOn = keyguardOn();
+        boolean canLaunchApp = isUserSetupComplete() && !keyguardOn;
         switch (gestureType) {
             case KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS:
                 if (complete) {
@@ -4166,7 +4156,7 @@
                 return true;
             case KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_ASSISTANT:
             case KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_VOICE_ASSISTANT:
-                if (complete) {
+                if (complete && canLaunchApp) {
                     launchAssistAction(Intent.EXTRA_ASSIST_INPUT_HINT_KEYBOARD,
                             deviceId, SystemClock.uptimeMillis(),
                             AssistUtils.INVOCATION_TYPE_UNKNOWN);
@@ -4179,7 +4169,7 @@
                 }
                 return true;
             case KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS:
-                if (complete) {
+                if (complete && canLaunchApp) {
                     showSystemSettings();
                 }
                 return true;
@@ -4282,7 +4272,7 @@
                 }
                 return true;
             case KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SEARCH:
-                if (complete) {
+                if (complete && canLaunchApp) {
                     launchTargetSearchActivity();
                 }
                 return true;
@@ -4363,63 +4353,20 @@
                 break;
             case KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION:
                 AppLaunchData data = event.getAppLaunchData();
-                if (complete && isUserSetupComplete() && !keyguardOn
-                        && data != null && mModifierShortcutManager.launchApplication(data)) {
+                if (complete && canLaunchApp && 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;
+            case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_DO_NOT_DISTURB:
+                NotificationManager nm = getNotificationService();
+                if (nm != null) {
+                    boolean isEnabled = nm.getZenMode() != Settings.Global.ZEN_MODE_OFF;
+                    nm.setZenMode(isEnabled ? Settings.Global.ZEN_MODE_OFF
+                                    : Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS, null,
+                            "Key gesture DND", true);
                 }
-                break;
-            case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MOUSE_KEYS:
-                if (InputSettings.isAccessibilityMouseKeysFeatureFlagEnabled()
-                        && keyboardA11yShortcutControl()) {
-                    if (complete) {
-                        final boolean mouseKeysEnabled =
-                                InputSettings.isAccessibilityMouseKeysEnabled(
-                                        mContext);
-                        InputSettings.setAccessibilityMouseKeysEnabled(mContext,
-                                !mouseKeysEnabled);
-                    }
-                    return true;
-                }
-                break;
-            case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_STICKY_KEYS:
-                if (InputSettings.isAccessibilityStickyKeysFeatureEnabled()
-                        && keyboardA11yShortcutControl()) {
-                    if (complete) {
-                        final boolean stickyKeysEnabled =
-                                InputSettings.isAccessibilityStickyKeysEnabled(mContext);
-                        InputSettings.setAccessibilityStickyKeysEnabled(mContext,
-                                !stickyKeysEnabled);
-                    }
-                    return true;
-                }
-                break;
-            case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_SLOW_KEYS:
-                if (InputSettings.isAccessibilitySlowKeysFeatureFlagEnabled()
-                        && keyboardA11yShortcutControl()) {
-                    if (complete) {
-                        final boolean slowKeysEnabled =
-                                InputSettings.isAccessibilitySlowKeysEnabled(mContext);
-                        InputSettings.setAccessibilitySlowKeysThreshold(mContext,
-                                slowKeysEnabled ? 0
-                                        : InputSettings.DEFAULT_SLOW_KEYS_THRESHOLD_MILLIS);
-                    }
-                    return true;
-                }
-                break;
+                return true;
         }
         return false;
     }
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 a6948fc..a975da3 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
@@ -31,7 +31,7 @@
     name: "framework_wakelock_info"
     namespace: "power"
     description: "Feature flag to enable statsd pulling of FrameworkWakelockInfo atoms"
-    bug: "352602149"
+    bug: "380847722"
 }
 
 flag {
diff --git a/services/core/java/com/android/server/power/hint/HintManagerService.java b/services/core/java/com/android/server/power/hint/HintManagerService.java
index 137ea06..aae7417 100644
--- a/services/core/java/com/android/server/power/hint/HintManagerService.java
+++ b/services/core/java/com/android/server/power/hint/HintManagerService.java
@@ -80,6 +80,7 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.lang.reflect.Field;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
@@ -340,6 +341,8 @@
         supportInfo.headroom = new SupportInfo.HeadroomSupportInfo();
         supportInfo.headroom.isCpuSupported = false;
         supportInfo.headroom.isGpuSupported = false;
+
+        supportInfo.compositionData = new SupportInfo.CompositionDataSupportInfo();
         if (isHintSessionSupported()) {
             if (mPowerHalVersion == 4) {
                 // Assume we support the V4 hints & modes unless specified
@@ -1480,6 +1483,7 @@
             if (!mSupportInfo.headroom.isCpuSupported) {
                 throw new UnsupportedOperationException();
             }
+            checkCpuHeadroomParams(params);
             final CpuHeadroomParams halParams = new CpuHeadroomParams();
             halParams.tids = new int[]{Binder.getCallingPid()};
             halParams.calculationType = params.calculationType;
@@ -1487,10 +1491,6 @@
             if (params.usesDeviceHeadroom) {
                 halParams.tids = new int[]{};
             } else if (params.tids != null && params.tids.length > 0) {
-                if (params.tids.length > 5) {
-                    throw new IllegalArgumentException(
-                            "More than 5 TIDs is requested: " + params.tids.length);
-                }
                 if (SystemProperties.getBoolean(PROPERTY_CHECK_HEADROOM_TID, true)) {
                     final int tgid = Process.getThreadGroupLeader(Binder.getCallingPid());
                     for (int tid : params.tids) {
@@ -1530,11 +1530,45 @@
             }
         }
 
+        private void checkCpuHeadroomParams(CpuHeadroomParamsInternal params) {
+            boolean calculationTypeMatched = false;
+            try {
+                for (final Field field :
+                        CpuHeadroomParams.CalculationType.class.getDeclaredFields()) {
+                    if (field.getType() == byte.class) {
+                        byte value = field.getByte(null);
+                        if (value == params.calculationType) {
+                            calculationTypeMatched = true;
+                            break;
+                        }
+                    }
+                }
+            } catch (IllegalAccessException e) {
+                Slog.wtf(TAG, "Checking the calculation type was unexpectedly not allowed");
+            }
+            if (!calculationTypeMatched) {
+                throw new IllegalArgumentException(
+                        "Unknown CPU headroom calculation type " + (int) params.calculationType);
+            }
+            if (params.calculationWindowMillis < 50 || params.calculationWindowMillis > 10000) {
+                throw new IllegalArgumentException(
+                        "Invalid CPU headroom calculation window, expected [50, 10000] but got "
+                                + params.calculationWindowMillis);
+            }
+            if (!params.usesDeviceHeadroom) {
+                if (params.tids != null && params.tids.length > 5) {
+                    throw new IllegalArgumentException(
+                            "More than 5 TIDs requested: " + params.tids.length);
+                }
+            }
+        }
+
         @Override
         public GpuHeadroomResult getGpuHeadroom(@NonNull GpuHeadroomParamsInternal params) {
             if (!mSupportInfo.headroom.isGpuSupported) {
                 throw new UnsupportedOperationException();
             }
+            checkGpuHeadroomParams(params);
             final GpuHeadroomParams halParams = new GpuHeadroomParams();
             halParams.calculationType = params.calculationType;
             halParams.calculationWindowMillis = params.calculationWindowMillis;
@@ -1565,6 +1599,33 @@
             }
         }
 
+        private void checkGpuHeadroomParams(GpuHeadroomParamsInternal params) {
+            boolean calculationTypeMatched = false;
+            try {
+                for (final Field field :
+                        GpuHeadroomParams.CalculationType.class.getDeclaredFields()) {
+                    if (field.getType() == byte.class) {
+                        byte value = field.getByte(null);
+                        if (value == params.calculationType) {
+                            calculationTypeMatched = true;
+                            break;
+                        }
+                    }
+                }
+            } catch (IllegalAccessException e) {
+                Slog.wtf(TAG, "Checking the calculation type was unexpectedly not allowed");
+            }
+            if (!calculationTypeMatched) {
+                throw new IllegalArgumentException(
+                        "Unknown GPU headroom calculation type " + (int) params.calculationType);
+            }
+            if (params.calculationWindowMillis < 50 || params.calculationWindowMillis > 10000) {
+                throw new IllegalArgumentException(
+                        "Invalid GPU headroom calculation window, expected [50, 10000] but got "
+                                + params.calculationWindowMillis);
+            }
+        }
+
         @Override
         public long getCpuHeadroomMinIntervalMillis() {
             if (!mSupportInfo.headroom.isCpuSupported) {
@@ -1590,6 +1651,7 @@
             mSessionManager = ISessionManager.Stub.asInterface(sessionManager);
         }
 
+        @Override
         public IHintManager.HintManagerClientData
                 registerClient(@NonNull IHintManager.IHintManagerClient clientBinder) {
             IHintManager.HintManagerClientData out = new IHintManager.HintManagerClientData();
diff --git a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
index 6f18107..95690cd 100644
--- a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
+++ b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
@@ -600,13 +600,7 @@
         private final int mFlags;
         private final Long mDefaultPowerStatsThrottlePeriod;
         private final Map<String, Long> mPowerStatsThrottlePeriods;
-
-        @VisibleForTesting
-        public BatteryStatsConfig() {
-            mFlags = 0;
-            mDefaultPowerStatsThrottlePeriod = 0L;
-            mPowerStatsThrottlePeriods = Map.of();
-        }
+        private final int mMaxHistorySizeBytes;
 
         private BatteryStatsConfig(Builder builder) {
             int flags = 0;
@@ -619,6 +613,7 @@
             mFlags = flags;
             mDefaultPowerStatsThrottlePeriod = builder.mDefaultPowerStatsThrottlePeriod;
             mPowerStatsThrottlePeriods = builder.mPowerStatsThrottlePeriods;
+            mMaxHistorySizeBytes = builder.mMaxHistorySizeBytes;
         }
 
         /**
@@ -648,18 +643,24 @@
                     mDefaultPowerStatsThrottlePeriod);
         }
 
+        public int getMaxHistorySizeBytes() {
+            return mMaxHistorySizeBytes;
+        }
+
         /**
          * Builder for BatteryStatsConfig
          */
         public static class Builder {
             private boolean mResetOnUnplugHighBatteryLevel;
             private boolean mResetOnUnplugAfterSignificantCharge;
-            public static final long DEFAULT_POWER_STATS_THROTTLE_PERIOD =
+            private static final long DEFAULT_POWER_STATS_THROTTLE_PERIOD =
                     TimeUnit.HOURS.toMillis(1);
-            public static final long DEFAULT_POWER_STATS_THROTTLE_PERIOD_CPU =
+            private static final long DEFAULT_POWER_STATS_THROTTLE_PERIOD_CPU =
                     TimeUnit.MINUTES.toMillis(1);
+            private static final int DEFAULT_MAX_HISTORY_SIZE = 4 * 1024 * 1024;
             private long mDefaultPowerStatsThrottlePeriod = DEFAULT_POWER_STATS_THROTTLE_PERIOD;
             private final Map<String, Long> mPowerStatsThrottlePeriods = new HashMap<>();
+            private int mMaxHistorySizeBytes = DEFAULT_MAX_HISTORY_SIZE;
 
             public Builder() {
                 mResetOnUnplugHighBatteryLevel = true;
@@ -712,6 +713,15 @@
                 mDefaultPowerStatsThrottlePeriod = periodMs;
                 return this;
             }
+
+            /**
+             * Sets the maximum amount of disk space, in bytes, that battery history can
+             * utilize. As this space fills up, the oldest history chunks must be expunged.
+             */
+            public Builder setMaxHistorySizeBytes(int maxHistorySizeBytes) {
+                mMaxHistorySizeBytes = maxHistorySizeBytes;
+                return this;
+            }
         }
     }
 
@@ -5723,7 +5733,9 @@
                     displayStats.screenDozeTimer.stopRunningLocked(elapsedRealtimeMs);
                     shouldScheduleSync = true;
                     break;
-                case Display.STATE_OFF: // fallthrough
+                case Display.STATE_OFF:
+                    shouldScheduleSync = true;
+                    break;
                 case Display.STATE_UNKNOWN:
                     // Not tracked by timers.
                     break;
@@ -5756,7 +5768,9 @@
                     displayStats.screenDozeTimer.startRunningLocked(elapsedRealtimeMs);
                     shouldScheduleSync = true;
                     break;
-                case Display.STATE_OFF: // fallthrough
+                case Display.STATE_OFF:
+                    shouldScheduleSync = true;
+                    break;
                 case Display.STATE_UNKNOWN:
                     // Not tracked by timers.
                     break;
@@ -5873,7 +5887,7 @@
 
         if (shouldScheduleSync) {
             if (mPowerStatsCollectorEnabled.get(BatteryConsumer.POWER_COMPONENT_SCREEN)) {
-                mScreenPowerStatsCollector.schedule();
+                mScreenPowerStatsCollector.onScreenStateChange();
             } else {
                 final int numDisplays = mPerDisplayBatteryStats.length;
                 final int[] displayStates = new int[numDisplays];
@@ -11421,7 +11435,7 @@
         }
 
         mHistory = new BatteryStatsHistory(null /* historyBuffer */, systemDir,
-                mConstants.MAX_HISTORY_FILES, mConstants.MAX_HISTORY_BUFFER, mStepDetailsCalculator,
+                mConstants.MAX_HISTORY_SIZE, mConstants.MAX_HISTORY_BUFFER, mStepDetailsCalculator,
                 mClock, mMonotonicClock, traceDelegate, eventLogger);
 
         mCpuPowerStatsCollector = new CpuPowerStatsCollector(mPowerStatsCollectorInjector);
@@ -11443,7 +11457,7 @@
         mWifiPowerStatsCollector.addConsumer(this::recordPowerStats);
 
         mBluetoothPowerStatsCollector = new BluetoothPowerStatsCollector(
-                mPowerStatsCollectorInjector);
+                mPowerStatsCollectorInjector, this::onBluetoothPowerStatsRetrieved);
         mBluetoothPowerStatsCollector.addConsumer(this::recordPowerStats);
 
         mCameraPowerStatsCollector = new CameraPowerStatsCollector(mPowerStatsCollectorInjector);
@@ -11966,9 +11980,8 @@
         return mNextMaxDailyDeadlineMs;
     }
 
-    @GuardedBy("this")
     public int getHistoryTotalSize() {
-        return mConstants.MAX_HISTORY_BUFFER * mConstants.MAX_HISTORY_FILES;
+        return mHistory.getMaxHistorySize();
     }
 
     public int getHistoryUsedSize() {
@@ -13413,6 +13426,13 @@
     private final BluetoothActivityInfoCache mLastBluetoothActivityInfo
             = new BluetoothActivityInfoCache();
 
+    private void onBluetoothPowerStatsRetrieved(BluetoothActivityEnergyInfo info,
+            long elapsedRealtimeMs, long uptimeMs) {
+        // Do not populate consumed energy, because energy attribution is done by
+        // BluetoothPowerStatsProcessor.
+        updateBluetoothStateLocked(info, POWER_DATA_UNAVAILABLE, elapsedRealtimeMs, uptimeMs);
+    }
+
     /**
      * Distribute Bluetooth energy info and network traffic to apps.
      *
@@ -13421,10 +13441,6 @@
     @GuardedBy("this")
     public void updateBluetoothStateLocked(@Nullable final BluetoothActivityEnergyInfo info,
             final long consumedChargeUC, long elapsedRealtimeMs, long uptimeMs) {
-        if (mBluetoothPowerStatsCollector.isEnabled()) {
-            return;
-        }
-
         if (DEBUG_ENERGY) {
             Slog.d(TAG, "Updating bluetooth stats: " + info);
         }
@@ -16094,7 +16110,7 @@
                 = "battery_level_collection_delay_ms";
         public static final String KEY_PROC_STATE_CHANGE_COLLECTION_DELAY_MS =
                 "procstate_change_collection_delay_ms";
-        public static final String KEY_MAX_HISTORY_FILES = "max_history_files";
+        public static final String KEY_MAX_HISTORY_SIZE = "max_history_size";
         public static final String KEY_MAX_HISTORY_BUFFER_KB = "max_history_buffer_kb";
         public static final String KEY_BATTERY_CHARGED_DELAY_MS =
                 "battery_charged_delay_ms";
@@ -16145,9 +16161,7 @@
         private static final long DEFAULT_EXTERNAL_STATS_COLLECTION_RATE_LIMIT_MS = 600_000;
         private static final long DEFAULT_BATTERY_LEVEL_COLLECTION_DELAY_MS = 300_000;
         private static final long DEFAULT_PROC_STATE_CHANGE_COLLECTION_DELAY_MS = 60_000;
-        private static final int DEFAULT_MAX_HISTORY_FILES = 32;
         private static final int DEFAULT_MAX_HISTORY_BUFFER_KB = 128; /*Kilo Bytes*/
-        private static final int DEFAULT_MAX_HISTORY_FILES_LOW_RAM_DEVICE = 64;
         private static final int DEFAULT_MAX_HISTORY_BUFFER_LOW_RAM_DEVICE_KB = 64; /*Kilo Bytes*/
         private static final int DEFAULT_BATTERY_CHARGED_DELAY_MS = 900000; /* 15 min */
         private static final int DEFAULT_BATTERY_CHARGING_ENFORCE_LEVEL = 90;
@@ -16169,7 +16183,7 @@
                 = DEFAULT_BATTERY_LEVEL_COLLECTION_DELAY_MS;
         public long PROC_STATE_CHANGE_COLLECTION_DELAY_MS =
                 DEFAULT_PROC_STATE_CHANGE_COLLECTION_DELAY_MS;
-        public int MAX_HISTORY_FILES;
+        public int MAX_HISTORY_SIZE;
         public int MAX_HISTORY_BUFFER; /*Bytes*/
         public int BATTERY_CHARGED_DELAY_MS = DEFAULT_BATTERY_CHARGED_DELAY_MS;
         public int BATTERY_CHARGING_ENFORCE_LEVEL = DEFAULT_BATTERY_CHARGING_ENFORCE_LEVEL;
@@ -16185,12 +16199,11 @@
         public Constants(Handler handler) {
             super(handler);
             if (isLowRamDevice()) {
-                MAX_HISTORY_FILES = DEFAULT_MAX_HISTORY_FILES_LOW_RAM_DEVICE;
                 MAX_HISTORY_BUFFER = DEFAULT_MAX_HISTORY_BUFFER_LOW_RAM_DEVICE_KB * 1024;
             } else {
-                MAX_HISTORY_FILES = DEFAULT_MAX_HISTORY_FILES;
                 MAX_HISTORY_BUFFER = DEFAULT_MAX_HISTORY_BUFFER_KB * 1024;
             }
+            MAX_HISTORY_SIZE = mBatteryStatsConfig.getMaxHistorySizeBytes();
         }
 
         public void startObserving(ContentResolver resolver) {
@@ -16253,13 +16266,23 @@
                 PROC_STATE_CHANGE_COLLECTION_DELAY_MS = mParser.getLong(
                         KEY_PROC_STATE_CHANGE_COLLECTION_DELAY_MS,
                         DEFAULT_PROC_STATE_CHANGE_COLLECTION_DELAY_MS);
-                MAX_HISTORY_FILES = mParser.getInt(KEY_MAX_HISTORY_FILES,
-                        isLowRamDevice() ? DEFAULT_MAX_HISTORY_FILES_LOW_RAM_DEVICE
-                                : DEFAULT_MAX_HISTORY_FILES);
                 MAX_HISTORY_BUFFER = mParser.getInt(KEY_MAX_HISTORY_BUFFER_KB,
                         isLowRamDevice() ? DEFAULT_MAX_HISTORY_BUFFER_LOW_RAM_DEVICE_KB
                                 : DEFAULT_MAX_HISTORY_BUFFER_KB)
                         * 1024;
+                int maxHistorySize = mParser.getInt(KEY_MAX_HISTORY_SIZE, -1);
+                if (maxHistorySize == -1) {
+                    // Process the deprecated max_history_files parameter for compatibility
+                    int maxHistoryFiles = mParser.getInt("max_history_files", -1);
+                    if (maxHistoryFiles != -1) {
+                        maxHistorySize = maxHistoryFiles * MAX_HISTORY_BUFFER;
+                    }
+                }
+                if (maxHistorySize == -1) {
+                    maxHistorySize = mBatteryStatsConfig.getMaxHistorySizeBytes();
+                }
+                MAX_HISTORY_SIZE = maxHistorySize;
+
                 final String perUidModemModel = mParser.getString(KEY_PER_UID_MODEM_POWER_MODEL,
                         "");
                 PER_UID_MODEM_MODEL = getPerUidModemModel(perUidModemModel);
@@ -16284,7 +16307,7 @@
          */
         @VisibleForTesting
         public void onChange() {
-            mHistory.setMaxHistoryFiles(MAX_HISTORY_FILES);
+            mHistory.setMaxHistorySize(MAX_HISTORY_SIZE);
             mHistory.setMaxHistoryBufferSize(MAX_HISTORY_BUFFER);
         }
 
@@ -16347,8 +16370,8 @@
             pw.println(BATTERY_LEVEL_COLLECTION_DELAY_MS);
             pw.print(KEY_PROC_STATE_CHANGE_COLLECTION_DELAY_MS); pw.print("=");
             pw.println(PROC_STATE_CHANGE_COLLECTION_DELAY_MS);
-            pw.print(KEY_MAX_HISTORY_FILES); pw.print("=");
-            pw.println(MAX_HISTORY_FILES);
+            pw.print(KEY_MAX_HISTORY_SIZE); pw.print("=");
+            pw.println(MAX_HISTORY_SIZE);
             pw.print(KEY_MAX_HISTORY_BUFFER_KB); pw.print("=");
             pw.println(MAX_HISTORY_BUFFER/1024);
             pw.print(KEY_BATTERY_CHARGED_DELAY_MS); pw.print("=");
diff --git a/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java b/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java
index 63e8d99..8c588b4 100644
--- a/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java
+++ b/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java
@@ -195,23 +195,22 @@
             mLastAccumulationMonotonicHistorySize = historySize;
         }
 
-        handler.post(() -> accumulateBatteryUsageStats(stats));
+        // No need to store the accumulated stats asynchronously, as the entire accumulation
+        // operation is async
+        handler.post(() -> accumulateBatteryUsageStats(stats, false));
     }
 
     /**
      * Computes BatteryUsageStats for the period since the last accumulated stats were stored,
-     * adds them to the accumulated stats and saves the result.
+     * adds them to the accumulated stats and asynchronously saves the result.
      */
     public void accumulateBatteryUsageStats(BatteryStatsImpl stats) {
-        AccumulatedBatteryUsageStats accumulatedStats = loadAccumulatedBatteryUsageStats();
+        accumulateBatteryUsageStats(stats, true);
+    }
 
-        final BatteryUsageStatsQuery query = new BatteryUsageStatsQuery.Builder()
-                .setMaxStatsAgeMs(0)
-                .includeProcessStateData()
-                .includePowerStateData()
-                .includeScreenStateData()
-                .build();
-        updateAccumulatedBatteryUsageStats(accumulatedStats, stats, query);
+    private void accumulateBatteryUsageStats(BatteryStatsImpl stats, boolean storeAsync) {
+        AccumulatedBatteryUsageStats accumulatedStats = loadAccumulatedBatteryUsageStats();
+        updateAccumulatedBatteryUsageStats(accumulatedStats, stats);
 
         PowerStatsSpan powerStatsSpan = new PowerStatsSpan(AccumulatedBatteryUsageStatsSection.ID);
         powerStatsSpan.addSection(
@@ -220,8 +219,13 @@
                 accumulatedStats.startWallClockTime,
                 accumulatedStats.endMonotonicTime - accumulatedStats.startMonotonicTime);
         mMonotonicClock.write();
-        mPowerStatsStore.storePowerStatsSpanAsync(powerStatsSpan,
-                accumulatedStats.builder::discard);
+        if (storeAsync) {
+            mPowerStatsStore.storePowerStatsSpanAsync(powerStatsSpan,
+                    accumulatedStats.builder::discard);
+        } else {
+            mPowerStatsStore.storePowerStatsSpan(powerStatsSpan);
+            accumulatedStats.builder.discard();
+        }
     }
 
     /**
@@ -269,7 +273,7 @@
         BatteryUsageStats batteryUsageStats;
         if ((query.getFlags()
                 & BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_ACCUMULATED) != 0) {
-            batteryUsageStats = getAccumulatedBatteryUsageStats(stats, query, currentTimeMs);
+            batteryUsageStats = getAccumulatedBatteryUsageStats(stats, query);
         } else if (query.getAggregatedToTimestamp() == 0) {
             BatteryUsageStats.Builder builder = computeBatteryUsageStats(stats, query,
                     query.getMonotonicStartTime(),
@@ -288,9 +292,13 @@
     }
 
     private BatteryUsageStats getAccumulatedBatteryUsageStats(BatteryStatsImpl stats,
-            BatteryUsageStatsQuery query, long currentTimeMs) {
+            BatteryUsageStatsQuery query) {
         AccumulatedBatteryUsageStats accumulatedStats = loadAccumulatedBatteryUsageStats();
-        updateAccumulatedBatteryUsageStats(accumulatedStats, stats, query);
+        if (accumulatedStats.endMonotonicTime == MonotonicClock.UNDEFINED
+                || mMonotonicClock.monotonicTime() - accumulatedStats.endMonotonicTime
+                > query.getMaxStatsAge()) {
+            updateAccumulatedBatteryUsageStats(accumulatedStats, stats);
+        }
         return accumulatedStats.builder.build();
     }
 
@@ -321,7 +329,7 @@
     }
 
     private void updateAccumulatedBatteryUsageStats(AccumulatedBatteryUsageStats accumulatedStats,
-            BatteryStatsImpl stats, BatteryUsageStatsQuery query) {
+            BatteryStatsImpl stats) {
         long startMonotonicTime = accumulatedStats.endMonotonicTime;
         if (startMonotonicTime == MonotonicClock.UNDEFINED) {
             startMonotonicTime = stats.getMonotonicStartTime();
@@ -333,6 +341,7 @@
             accumulatedStats.builder = new BatteryUsageStats.Builder(
                     stats.getCustomEnergyConsumerNames(), true, true, true, 0);
             accumulatedStats.startWallClockTime = stats.getStartClockTime();
+            accumulatedStats.startMonotonicTime = stats.getMonotonicStartTime();
             accumulatedStats.builder.setStatsStartTimestamp(accumulatedStats.startWallClockTime);
         }
 
@@ -342,7 +351,7 @@
         accumulatedStats.builder.setStatsDuration(endWallClockTime - startMonotonicTime);
 
         mPowerAttributor.estimatePowerConsumption(accumulatedStats.builder, stats.getHistory(),
-                startMonotonicTime, MonotonicClock.UNDEFINED);
+                startMonotonicTime, endMonotonicTime);
 
         populateGeneralInfo(accumulatedStats.builder, stats);
     }
diff --git a/services/core/java/com/android/server/power/stats/BluetoothPowerStatsCollector.java b/services/core/java/com/android/server/power/stats/BluetoothPowerStatsCollector.java
index d7aa987..c12ae63 100644
--- a/services/core/java/com/android/server/power/stats/BluetoothPowerStatsCollector.java
+++ b/services/core/java/com/android/server/power/stats/BluetoothPowerStatsCollector.java
@@ -15,6 +15,7 @@
  */
 package com.android.server.power.stats;
 
+import android.annotation.Nullable;
 import android.bluetooth.BluetoothActivityEnergyInfo;
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.UidTraffic;
@@ -41,7 +42,10 @@
 
     private static final long BLUETOOTH_ACTIVITY_REQUEST_TIMEOUT = 20000;
 
-    private static final long ENERGY_UNSPECIFIED = -1;
+    interface Observer {
+        void onBluetoothPowerStatsRetrieved(@Nullable BluetoothActivityEnergyInfo info,
+                long elapsedRealtimeMs, long uptimeMs);
+    }
 
     public interface BluetoothStatsRetriever {
         interface Callback {
@@ -65,6 +69,7 @@
     }
 
     private final Injector mInjector;
+    private final Observer mObserver;
 
     private com.android.server.power.stats.format.BluetoothPowerStatsLayout mLayout;
     private boolean mIsInitialized;
@@ -89,13 +94,14 @@
 
     private final SparseArray<UidStats> mUidStats = new SparseArray<>();
 
-    public BluetoothPowerStatsCollector(Injector injector) {
+    public BluetoothPowerStatsCollector(Injector injector, @Nullable Observer observer) {
         super(injector.getHandler(),  injector.getPowerStatsCollectionThrottlePeriod(
                         BatteryConsumer.powerComponentIdToString(
                                 BatteryConsumer.POWER_COMPONENT_BLUETOOTH)),
                 injector.getUidResolver(),
                 injector.getClock());
         mInjector = injector;
+        mObserver = observer;
     }
 
     @Override
@@ -146,15 +152,20 @@
         Arrays.fill(mDeviceStats, 0);
         mPowerStats.uidStats.clear();
 
-        collectBluetoothActivityInfo();
+        BluetoothActivityEnergyInfo activityInfo = collectBluetoothActivityInfo();
         collectBluetoothScanStats();
 
         mConsumedEnergyHelper.collectConsumedEnergy(mPowerStats, mLayout);
 
+        if (mObserver != null) {
+            mObserver.onBluetoothPowerStatsRetrieved(activityInfo, mClock.elapsedRealtime(),
+                    mClock.uptimeMillis());
+        }
+
         return mPowerStats;
     }
 
-    private void collectBluetoothActivityInfo() {
+    private BluetoothActivityEnergyInfo collectBluetoothActivityInfo() {
         CompletableFuture<BluetoothActivityEnergyInfo> immediateFuture = new CompletableFuture<>();
         boolean success = mBluetoothStatsRetriever.requestControllerActivityEnergyInfo(
                 Runnable::run,
@@ -173,7 +184,7 @@
                 });
 
         if (!success) {
-            return;
+            return null;
         }
 
         BluetoothActivityEnergyInfo activityInfo;
@@ -186,7 +197,7 @@
         }
 
         if (activityInfo == null) {
-            return;
+            return null;
         }
 
         long rxTime = activityInfo.getControllerRxTimeMillis();
@@ -241,6 +252,8 @@
                 mLayout.setUidTxBytes(stats, txDelta);
             }
         }
+
+        return activityInfo;
     }
 
     private void collectBluetoothScanStats() {
diff --git a/services/core/java/com/android/server/power/stats/ScreenPowerStatsCollector.java b/services/core/java/com/android/server/power/stats/ScreenPowerStatsCollector.java
index c38904f..90039e8 100644
--- a/services/core/java/com/android/server/power/stats/ScreenPowerStatsCollector.java
+++ b/services/core/java/com/android/server/power/stats/ScreenPowerStatsCollector.java
@@ -111,6 +111,22 @@
         return true;
     }
 
+    /**
+     * Must be called whenever the screen state (on/off/doze) changes.
+     */
+    public void onScreenStateChange() {
+        if (ensureInitialized() && mConsumedEnergyHelper.getEnergyConsumerCount() != 0) {
+            // Sync power monitor reading immediately, because the estimation algorithm
+            // distributes consumed power proportionally between screen states.
+            // Since screen power consumption differs dramatically between different states,
+            // this would lead an overestimation in the screen-off state.
+            forceSchedule();
+            return;
+        }
+        // Perhaps schedule a sync, allowing throttling
+        schedule();
+    }
+
     @Override
     public PowerStats collectStats() {
         if (!ensureInitialized()) {
@@ -126,7 +142,7 @@
             long screenOnTimeMs = mScreenUsageTimeRetriever.getScreenOnTimeMs(display);
             if (!mFirstSample) {
                 mLayout.setScreenOnDuration(mPowerStats.stats, display,
-                        screenOnTimeMs - mLastScreenOnTime[display]);
+                        Math.max(0, screenOnTimeMs - mLastScreenOnTime[display]));
             }
             mLastScreenOnTime[display] = screenOnTimeMs;
 
@@ -135,14 +151,15 @@
                         mScreenUsageTimeRetriever.getBrightnessLevelTimeMs(display, level);
                 if (!mFirstSample) {
                     mLayout.setBrightnessLevelDuration(mPowerStats.stats, display, level,
-                            brightnessLevelTimeMs - mLastBrightnessLevelTime[display][level]);
+                            Math.max(0, brightnessLevelTimeMs
+                                    - mLastBrightnessLevelTime[display][level]));
                 }
                 mLastBrightnessLevelTime[display][level] = brightnessLevelTimeMs;
             }
             long screenDozeTimeMs = mScreenUsageTimeRetriever.getScreenDozeTimeMs(display);
             if (!mFirstSample) {
                 mLayout.setScreenDozeDuration(mPowerStats.stats, display,
-                        screenDozeTimeMs - mLastDozeTime[display]);
+                        Math.max(0, screenDozeTimeMs - mLastDozeTime[display]));
             }
             mLastDozeTime[display] = screenDozeTimeMs;
         }
@@ -162,7 +179,7 @@
             }
 
             mLayout.setUidTopActivityDuration(uidStats,
-                    mLayout.getUidTopActivityDuration(uidStats) + topActivityDuration);
+                    Math.max(0, mLayout.getUidTopActivityDuration(uidStats) + topActivityDuration));
         });
 
         long elapsedRealtime = mClock.elapsedRealtime();
diff --git a/services/core/java/com/android/server/power/stats/processor/PowerStatsAggregator.java b/services/core/java/com/android/server/power/stats/processor/PowerStatsAggregator.java
index dcdd3bd..1b864bb 100644
--- a/services/core/java/com/android/server/power/stats/processor/PowerStatsAggregator.java
+++ b/services/core/java/com/android/server/power/stats/processor/PowerStatsAggregator.java
@@ -23,6 +23,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.os.BatteryStatsHistory;
 import com.android.internal.os.BatteryStatsHistoryIterator;
+import com.android.internal.os.MonotonicClock;
 
 import java.util.function.Consumer;
 
@@ -169,10 +170,15 @@
                     }
                 }
             }
-            if (lastTime > baseTime) {
-                mStats.setDuration(lastTime - baseTime);
-                mStats.finish(lastTime);
-                consumer.accept(mStats);
+            if (startedSession) {
+                if (endTimeMs != MonotonicClock.UNDEFINED) {
+                    lastTime = endTimeMs;
+                }
+                if (lastTime > baseTime) {
+                    mStats.setDuration(lastTime - baseTime);
+                    mStats.finish(lastTime);
+                    consumer.accept(mStats);
+                }
             }
 
             mStats.reset();     // to free up memory
diff --git a/services/core/java/com/android/server/security/advancedprotection/features/DisallowCellular2GAdvancedProtectionHook.java b/services/core/java/com/android/server/security/advancedprotection/features/DisallowCellular2GAdvancedProtectionHook.java
index f51c25d..acdea88 100644
--- a/services/core/java/com/android/server/security/advancedprotection/features/DisallowCellular2GAdvancedProtectionHook.java
+++ b/services/core/java/com/android/server/security/advancedprotection/features/DisallowCellular2GAdvancedProtectionHook.java
@@ -48,7 +48,7 @@
         mTelephonyManager = context.getSystemService(TelephonyManager.class);
         mSubscriptionManager = context.getSystemService(SubscriptionManager.class);
 
-        setPolicy(enabled);
+        onAdvancedProtectionChanged(enabled);
     }
 
     @NonNull
@@ -94,7 +94,8 @@
         return false;
     }
 
-    private void setPolicy(boolean enabled) {
+    @Override
+    public void onAdvancedProtectionChanged(boolean enabled) {
         if (enabled) {
             Slog.d(TAG, "Setting DISALLOW_CELLULAR_2G_GLOBALLY restriction");
             mDevicePolicyManager.addUserRestrictionGlobally(
@@ -105,21 +106,4 @@
                     ADVANCED_PROTECTION_SYSTEM_ENTITY, UserManager.DISALLOW_CELLULAR_2G);
         }
     }
-
-    @Override
-    public void onAdvancedProtectionChanged(boolean enabled) {
-        setPolicy(enabled);
-
-        // Leave 2G disabled even if APM is disabled.
-        if (!enabled) {
-            for (TelephonyManager telephonyManager : getActiveTelephonyManagers()) {
-                long oldAllowedTypes =
-                        telephonyManager.getAllowedNetworkTypesForReason(
-                                TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G);
-                long newAllowedTypes = oldAllowedTypes & ~TelephonyManager.NETWORK_CLASS_BITMASK_2G;
-                telephonyManager.setAllowedNetworkTypesForReason(
-                        TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G, newAllowedTypes);
-            }
-        }
-    }
 }
diff --git a/services/core/java/com/android/server/security/advancedprotection/features/DisallowInstallUnknownSourcesAdvancedProtectionHook.java b/services/core/java/com/android/server/security/advancedprotection/features/DisallowInstallUnknownSourcesAdvancedProtectionHook.java
index bb523d6..59bb34d 100644
--- a/services/core/java/com/android/server/security/advancedprotection/features/DisallowInstallUnknownSourcesAdvancedProtectionHook.java
+++ b/services/core/java/com/android/server/security/advancedprotection/features/DisallowInstallUnknownSourcesAdvancedProtectionHook.java
@@ -19,24 +19,13 @@
 import static android.security.advancedprotection.AdvancedProtectionManager.ADVANCED_PROTECTION_SYSTEM_ENTITY;
 import static android.security.advancedprotection.AdvancedProtectionManager.FEATURE_ID_DISALLOW_INSTALL_UNKNOWN_SOURCES;
 
-import android.Manifest;
 import android.annotation.NonNull;
-import android.app.ActivityManagerInternal;
-import android.app.AppGlobals;
-import android.app.AppOpsManager;
 import android.app.admin.DevicePolicyManager;
 import android.content.Context;
-import android.content.pm.IPackageManager;
-import android.content.pm.PackageManager;
-import android.content.pm.UserInfo;
-import android.os.Process;
-import android.os.RemoteException;
 import android.os.UserManager;
 import android.security.advancedprotection.AdvancedProtectionFeature;
 import android.util.Slog;
 
-import com.android.server.LocalServices;
-
 /** @hide */
 public final class DisallowInstallUnknownSourcesAdvancedProtectionHook
         extends AdvancedProtectionHook {
@@ -45,24 +34,14 @@
     private final AdvancedProtectionFeature mFeature = new AdvancedProtectionFeature(
             FEATURE_ID_DISALLOW_INSTALL_UNKNOWN_SOURCES);
 
-    private final ActivityManagerInternal mActivityManagerInternal;
-    private final AppOpsManager mAppOpsManager;
     private final DevicePolicyManager mDevicePolicyManager;
-    private final IPackageManager mIPackageManager;
-    private final PackageManager mPackageManager;
-    private final UserManager mUserManager;
 
     public DisallowInstallUnknownSourcesAdvancedProtectionHook(@NonNull Context context,
             boolean enabled) {
         super(context, enabled);
-        mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
-        mAppOpsManager = context.getSystemService(AppOpsManager.class);
         mDevicePolicyManager = context.getSystemService(DevicePolicyManager.class);
-        mIPackageManager = AppGlobals.getPackageManager();
-        mUserManager = context.getSystemService(UserManager.class);
-        mPackageManager = context.getPackageManager();
 
-        setRestriction(enabled);
+        onAdvancedProtectionChanged(enabled);
     }
 
     @NonNull
@@ -76,7 +55,8 @@
         return true;
     }
 
-    private void setRestriction(boolean enabled) {
+    @Override
+    public void onAdvancedProtectionChanged(boolean enabled) {
         if (enabled) {
             Slog.d(TAG, "Setting DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY restriction");
             mDevicePolicyManager.addUserRestrictionGlobally(ADVANCED_PROTECTION_SYSTEM_ENTITY,
@@ -87,36 +67,4 @@
                     UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY);
         }
     }
-
-    @Override
-    public void onAdvancedProtectionChanged(boolean enabled) {
-        setRestriction(enabled);
-        if (enabled) return;
-
-        // Leave OP_REQUEST_INSTALL_PACKAGES disabled when APM is disabled.
-        Slog.d(TAG, "Setting all OP_REQUEST_INSTALL_PACKAGES to MODE_ERRORED");
-        for (UserInfo userInfo : mUserManager.getAliveUsers()) {
-            try {
-                final String[] packagesWithRequestInstallPermission = mIPackageManager
-                        .getAppOpPermissionPackages(
-                                Manifest.permission.REQUEST_INSTALL_PACKAGES, userInfo.id);
-                for (String packageName : packagesWithRequestInstallPermission) {
-                    try {
-                        int uid = mPackageManager.getPackageUidAsUser(packageName, userInfo.id);
-                        boolean isCallerInstrumented = mActivityManagerInternal
-                                .getInstrumentationSourceUid(uid) != Process.INVALID_UID;
-                        if (!isCallerInstrumented) {
-                            mAppOpsManager.setMode(AppOpsManager.OP_REQUEST_INSTALL_PACKAGES, uid,
-                                    packageName, AppOpsManager.MODE_ERRORED);
-                        }
-                    } catch (PackageManager.NameNotFoundException e) {
-                        Slog.e(TAG, "Couldn't retrieve uid for a package: " + e);
-                    }
-                }
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Couldn't retrieve packages with REQUEST_INSTALL_PACKAGES."
-                        + " getAppOpPermissionPackages() threw the following exception: " + e);
-            }
-        }
-    }
 }
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index 3f814f9..89c7a3d 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -57,6 +57,7 @@
 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_TRANSITION_REPORTED_DRAWN_NO_BUNDLE;
 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_TRANSITION_REPORTED_DRAWN_WITH_BUNDLE;
 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_TRANSITION_WARM_LAUNCH;
+import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_WINDOW_TRANSITIONS;
 import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__LETTERBOX_POSITION__NOT_LETTERBOXED_POSITION;
 import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_ASPECT_RATIO;
 import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_FIXED_ORIENTATION;
@@ -103,6 +104,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.MetricsLogger;
+import com.android.internal.protolog.ProtoLog;
 import com.android.internal.util.FrameworkStatsLog;
 import com.android.internal.util.LatencyTracker;
 import com.android.internal.util.function.pooled.PooledLambda;
@@ -110,6 +112,7 @@
 import com.android.server.LocalServices;
 import com.android.server.apphibernation.AppHibernationManagerInternal;
 import com.android.server.apphibernation.AppHibernationService;
+import com.android.window.flags.Flags;
 
 import java.util.ArrayList;
 import java.util.concurrent.TimeUnit;
@@ -388,6 +391,14 @@
                 return;
             }
             if (mLastLaunchedActivity != null) {
+                if (mLastLaunchedActivity.mLaunchCookie != null) {
+                    ProtoLog.v(WM_DEBUG_WINDOW_TRANSITIONS,
+                            "Transferring launch cookie=%s from=%s(%d) to=%s(%d)",
+                            mLastLaunchedActivity.mLaunchCookie,
+                            mLastLaunchedActivity.packageName,
+                            System.identityHashCode(mLastLaunchedActivity), r.packageName,
+                            System.identityHashCode(r));
+                }
                 // Transfer the launch cookie and launch root task because it is a consecutive
                 // launch event.
                 r.mLaunchCookie = mLastLaunchedActivity.mLaunchCookie;
@@ -778,11 +789,12 @@
      */
     private void updateSplitPairLaunches(@NonNull TransitionInfo info) {
         final Task launchedActivityTask = info.mLastLaunchedActivity.getTask();
-        final Task adjacentToLaunchedTask = launchedActivityTask.getAdjacentTask();
-        if (adjacentToLaunchedTask == null) {
+        final Task launchedSplitRootTask = launchedActivityTask.getTaskWithAdjacent();
+        if (launchedSplitRootTask == null) {
             // Not a part of a split pair
             return;
         }
+
         for (int i = mTransitionInfoList.size() - 1; i >= 0; i--) {
             final TransitionInfo otherInfo = mTransitionInfoList.get(i);
             if (otherInfo == info) {
@@ -790,7 +802,15 @@
             }
             final Task otherTask = otherInfo.mLastLaunchedActivity.getTask();
             // The adjacent task is the split root in which activities are started
-            if (otherTask.isDescendantOf(adjacentToLaunchedTask)) {
+            final boolean isDescendantOfAdjacent;
+            if (Flags.allowMultipleAdjacentTaskFragments()) {
+                isDescendantOfAdjacent = launchedSplitRootTask.forOtherAdjacentTasks(
+                        otherTask::isDescendantOf);
+            } else {
+                isDescendantOfAdjacent = otherTask.isDescendantOf(
+                        launchedSplitRootTask.getAdjacentTask());
+            }
+            if (isDescendantOfAdjacent) {
                 if (DEBUG_METRICS) {
                     Slog.i(TAG, "Found adjacent tasks t1=" + launchedActivityTask.mTaskId
                             + " t2=" + otherTask.mTaskId);
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 5989fc8..83b273c 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -137,6 +137,7 @@
 import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_STARTING_WINDOW;
 import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_STATES;
 import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_SWITCH;
+import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_WINDOW_TRANSITIONS;
 import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_WINDOW_TRANSITIONS_MIN;
 import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_ASPECT_RATIO;
 import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_FIXED_ORIENTATION;
@@ -248,7 +249,6 @@
 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES;
 import static com.android.server.wm.WindowManagerService.sEnableShellTransitions;
 import static com.android.server.wm.WindowState.LEGACY_POLICY_VISIBILITY;
-import static com.android.server.wm.WindowStateAnimator.HAS_DRAWN;
 
 import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
 import static org.xmlpull.v1.XmlPullParser.END_TAG;
@@ -2138,6 +2138,11 @@
             mHandoverLaunchDisplayId = options.getLaunchDisplayId();
             mLaunchCookie = options.getLaunchCookie();
             mLaunchRootTask = options.getLaunchRootTask();
+            if (mLaunchCookie != null) {
+                ProtoLog.v(WM_DEBUG_WINDOW_TRANSITIONS,
+                        "Activity created with launch cookie=%s act=%s(%d)",
+                        mLaunchCookie, packageName, System.identityHashCode(this));
+            }
         } else {
             mHasSceneTransition = false;
         }
@@ -2406,9 +2411,8 @@
             return false;
         }
 
-        final TaskSnapshot snapshot =
-                mWmService.mTaskSnapshotController.getSnapshot(task.mTaskId, task.mUserId,
-                        false /* restoreFromDisk */, false /* isLowResolution */);
+        final TaskSnapshot snapshot = mWmService.mTaskSnapshotController.getSnapshot(task.mTaskId,
+                false /* isLowResolution */);
         final int type = getStartingWindowType(newTask, taskSwitch, processRunning,
                 allowTaskSnapshot, activityCreated, activityAllDrawn, snapshot);
 
@@ -2650,7 +2654,8 @@
                 // Skip copy splash screen to client if it was resized, or the starting data already
                 // requested to be removed after transaction commit.
                 || (mStartingData != null && (mStartingData.mResizedFromTransfer
-                        || mStartingData.mRemoveAfterTransaction != AFTER_TRANSACTION_IDLE))
+                        || mStartingData.mRemoveAfterTransaction
+                        == AFTER_TRANSACTION_REMOVE_DIRECTLY))
                 || isRelaunching()) {
             return false;
         }
@@ -4142,6 +4147,10 @@
                     r -> r.mLaunchCookie == null && !r.finishing && r.isUid(getUid()),
                     this, false /* includeBoundary */, false /* traverseTopToBottom */);
             if (nextCookieTarget != null) {
+                ProtoLog.v(WM_DEBUG_WINDOW_TRANSITIONS,
+                        "Transferring launch cookie=%s on finish from=%s(%d) to=%s(%d)",
+                        mLaunchCookie, packageName, System.identityHashCode(this),
+                        nextCookieTarget.packageName, System.identityHashCode(nextCookieTarget));
                 nextCookieTarget.mLaunchCookie = mLaunchCookie;
                 mLaunchCookie = null;
             }
@@ -5607,18 +5616,6 @@
             // stopped, then we need to set up to wait for its windows to be ready.
             if (!isVisible() || mAppStopped) {
                 clearAllDrawn();
-                // Reset the draw state in order to prevent the starting window to be immediately
-                // dismissed when the app still has the surface.
-                if (!Flags.resetDrawStateOnClientInvisible()
-                        && !isVisible() && !isClientVisible()) {
-                    forAllWindows(w -> {
-                        if (w.mWinAnimator.mDrawState == HAS_DRAWN) {
-                            w.mWinAnimator.resetDrawState();
-                            // Force add to mResizingWindows, so the window will report drawn.
-                            w.forceReportingResized();
-                        }
-                    }, true /* traverseTopToBottom */);
-                }
             }
 
             // In the case where we are making an app visible but holding off for a transition,
@@ -5627,13 +5624,18 @@
             // drawn, they never will be, and we are sad.
             setClientVisible(true);
 
-            requestUpdateWallpaperIfNeeded();
+            if (!mWmService.mFlags.mEnsureWallpaperInTransitions) {
+                requestUpdateWallpaperIfNeeded();
+            }
 
             ProtoLog.v(WM_DEBUG_ADD_REMOVE, "No longer Stopped: %s", this);
             mAppStopped = false;
 
             transferStartingWindowFromHiddenAboveTokenIfNeeded();
         }
+        if (mWmService.mFlags.mEnsureWallpaperInTransitions) {
+            requestUpdateWallpaperIfNeeded();
+        }
 
         // Defer committing visibility until transition starts.
         if (isCollecting) {
@@ -8510,7 +8512,7 @@
         final boolean isFixedOrientationLetterboxAllowed = !getLaunchedFromBubble()
                 && (parentWindowingMode == WINDOWING_MODE_MULTI_WINDOW
                         || parentWindowingMode == WINDOWING_MODE_FULLSCREEN
-                        || AppCompatCameraPolicy.shouldCameraCompatControlOrientation(this)
+                        || AppCompatCameraPolicy.isFreeformLetterboxingForCameraAllowed(this)
                         // When starting to switch between PiP and fullscreen, the task is pinned
                         // and the activity is fullscreen. But only allow to apply letterbox if the
                         // activity is exiting PiP because an entered PiP should fill the task.
diff --git a/services/core/java/com/android/server/wm/ActivitySnapshotController.java b/services/core/java/com/android/server/wm/ActivitySnapshotController.java
index 57a0bb5..9aaa0e1 100644
--- a/services/core/java/com/android/server/wm/ActivitySnapshotController.java
+++ b/services/core/java/com/android/server/wm/ActivitySnapshotController.java
@@ -149,7 +149,8 @@
         for (int i = activities.length - 1; i >= 0; --i) {
             fileId ^= getSystemHashCode(activities[i]);
         }
-        return tmpUsf.mFileId == fileId ? mCache.getSnapshot(tmpUsf.mActivityIds.get(0)) : null;
+        return tmpUsf.mFileId == fileId
+                ? mCache.getSnapshotInner(tmpUsf.mActivityIds.get(0)) : null;
     }
 
     private void cleanUpUserFiles(int userId) {
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 2781592..acb9384 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -65,6 +65,7 @@
 
 import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_CONFIGURATION;
 import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_TASKS;
+import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_WINDOW_TRANSITIONS;
 import static com.android.server.pm.PackageArchiver.isArchivingEnabled;
 import static com.android.server.wm.ActivityRecord.State.RESUMED;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_PERMISSIONS_REVIEW;
@@ -3097,6 +3098,10 @@
         // options if set.
         if (mStartActivity.mLaunchCookie != null) {
             intentActivity.mLaunchCookie = mStartActivity.mLaunchCookie;
+            ProtoLog.v(WM_DEBUG_WINDOW_TRANSITIONS,
+                    "Updating launch cookie=%s act=%s(%d)",
+                    intentActivity.mLaunchCookie, intentActivity.packageName,
+                    System.identityHashCode(intentActivity));
         }
         if (mStartActivity.mPendingRemoteAnimation != null) {
             intentActivity.mPendingRemoteAnimation = mStartActivity.mPendingRemoteAnimation;
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index afa7ea1..5eee8ec 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -3996,15 +3996,14 @@
             }
             // Try to load snapshot from cache first, and add reference if the snapshot is in cache.
             final TaskSnapshot snapshot = mWindowManager.mTaskSnapshotController.getSnapshot(taskId,
-                    task.mUserId, false /* restoreFromDisk */, isLowResolution);
+                    isLowResolution, usage);
             if (snapshot != null) {
-                snapshot.addReference(usage);
                 return snapshot;
             }
         }
         // Don't call this while holding the lock as this operation might hit the disk.
-        return mWindowManager.mTaskSnapshotController.getSnapshot(taskId,
-                task.mUserId, true /* restoreFromDisk */, isLowResolution);
+        return mWindowManager.mTaskSnapshotController.getSnapshotFromDisk(taskId,
+                task.mUserId,  isLowResolution, usage);
     }
 
     @Override
@@ -4020,10 +4019,15 @@
                     Slog.w(TAG, "getTaskSnapshot: taskId=" + taskId + " not found");
                     return null;
                 }
+                final TaskSnapshot snapshot = mWindowManager.mTaskSnapshotController.getSnapshot(
+                        taskId, isLowResolution, TaskSnapshot.REFERENCE_WRITE_TO_PARCEL);
+                if (snapshot != null) {
+                    return snapshot;
+                }
             }
             // Don't call this while holding the lock as this operation might hit the disk.
-            return mWindowManager.mTaskSnapshotController.getSnapshot(taskId,
-                    task.mUserId, true /* restoreFromDisk */, isLowResolution);
+            return mWindowManager.mTaskSnapshotController.getSnapshotFromDisk(taskId,
+                    task.mUserId, isLowResolution, TaskSnapshot.REFERENCE_WRITE_TO_PARCEL);
         } finally {
             Binder.restoreCallingIdentity(ident);
         }
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index a077a0b..0aff1de 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -54,6 +54,7 @@
 
 import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_STATES;
 import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_TASKS;
+import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_WINDOW_TRANSITIONS;
 import static com.android.server.wm.ActivityRecord.State.PAUSED;
 import static com.android.server.wm.ActivityRecord.State.PAUSING;
 import static com.android.server.wm.ActivityRecord.State.RESTARTING_PROCESS;
@@ -1268,7 +1269,8 @@
             // Checks if the caller can be shown in the given public display.
             int userId = UserHandle.getUserId(callingUid);
             int displayId = display.getDisplayId();
-            boolean allowed = mWindowManager.mUmInternal.isUserVisible(userId, displayId);
+            boolean allowed = userId == UserHandle.USER_SYSTEM
+                    || mWindowManager.mUmInternal.isUserVisible(userId, displayId);
             ProtoLog.d(WM_DEBUG_TASKS,
                     "Launch on display check: %s launch for userId=%d on displayId=%d",
                     (allowed ? "allow" : "disallow"), userId, displayId);
@@ -2842,6 +2844,10 @@
                         targetActivity.applyOptionsAnimation();
                         if (activityOptions != null && activityOptions.getLaunchCookie() != null) {
                             targetActivity.mLaunchCookie = activityOptions.getLaunchCookie();
+                            ProtoLog.v(WM_DEBUG_WINDOW_TRANSITIONS,
+                                    "Updating launch cookie=%s for start from recents act=%s(%d)",
+                                    targetActivity.mLaunchCookie, targetActivity.packageName,
+                                    System.identityHashCode(targetActivity));
                         }
                     } finally {
                         mActivityMetricsLogger.notifyActivityLaunched(launchingState,
diff --git a/services/core/java/com/android/server/wm/AppCompatCameraPolicy.java b/services/core/java/com/android/server/wm/AppCompatCameraPolicy.java
index 8be66cc..9547e5cc 100644
--- a/services/core/java/com/android/server/wm/AppCompatCameraPolicy.java
+++ b/services/core/java/com/android/server/wm/AppCompatCameraPolicy.java
@@ -193,6 +193,17 @@
     }
 
     // TODO(b/369070416): have policies implement the same interface.
+    static boolean isFreeformLetterboxingForCameraAllowed(@NonNull ActivityRecord activity) {
+        final AppCompatCameraPolicy cameraPolicy = getAppCompatCameraPolicy(activity);
+        if (cameraPolicy == null) {
+            return false;
+        }
+        return cameraPolicy.mCameraCompatFreeformPolicy != null
+                        && cameraPolicy.mCameraCompatFreeformPolicy
+                                .isFreeformLetterboxingForCameraAllowed(activity);
+    }
+
+    // TODO(b/369070416): have policies implement the same interface.
     static boolean shouldCameraCompatControlAspectRatio(@NonNull ActivityRecord activity) {
         final AppCompatCameraPolicy cameraPolicy = getAppCompatCameraPolicy(activity);
         if (cameraPolicy == null) {
diff --git a/services/core/java/com/android/server/wm/AppCompatOrientationPolicy.java b/services/core/java/com/android/server/wm/AppCompatOrientationPolicy.java
index 7aed33d..16e2029 100644
--- a/services/core/java/com/android/server/wm/AppCompatOrientationPolicy.java
+++ b/services/core/java/com/android/server/wm/AppCompatOrientationPolicy.java
@@ -23,6 +23,7 @@
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_USER;
 import static android.content.pm.ActivityInfo.isFixedOrientation;
 import static android.content.pm.ActivityInfo.isFixedOrientationLandscape;
+import static android.content.pm.ActivityInfo.isFixedOrientationPortrait;
 import static android.content.pm.ActivityInfo.screenOrientationToString;
 
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
@@ -66,6 +67,7 @@
         final boolean shouldCameraCompatControlOrientation =
                 AppCompatCameraPolicy.shouldCameraCompatControlOrientation(mActivityRecord);
         if (hasFullscreenOverride && isIgnoreOrientationRequestEnabled
+                && (isFixedOrientationLandscape(candidate) || isFixedOrientationPortrait(candidate))
                 // Do not override orientation to fullscreen for camera activities.
                 // Fixed-orientation activities are rarely tested in other orientations, and it
                 // often results in sideways or stretched previews. As the camera compat treatment
diff --git a/services/core/java/com/android/server/wm/AppCompatSizeCompatModePolicy.java b/services/core/java/com/android/server/wm/AppCompatSizeCompatModePolicy.java
index d0d3d43..f3b043b 100644
--- a/services/core/java/com/android/server/wm/AppCompatSizeCompatModePolicy.java
+++ b/services/core/java/com/android/server/wm/AppCompatSizeCompatModePolicy.java
@@ -60,7 +60,7 @@
 
     /**
      * The precomputed display insets for resolving configuration. It will be non-null if
-     * {@link #shouldCreateAppCompatDisplayInsets} returns {@code true}.
+     * {@link ActivityRecord#shouldCreateAppCompatDisplayInsets} returns {@code true}.
      */
     @Nullable
     private AppCompatDisplayInsets mAppCompatDisplayInsets;
@@ -84,7 +84,7 @@
     }
 
     /**
-     * @return The {@code true} if the current instance has {@link mAppCompatDisplayInsets} without
+     * @return The {@code true} if the current instance has {@link #mAppCompatDisplayInsets} without
      * considering the inheritance implemented in {@link #getAppCompatDisplayInsets()}
      */
     boolean hasAppCompatDisplayInsetsWithoutInheritance() {
diff --git a/services/core/java/com/android/server/wm/AppCompatUtils.java b/services/core/java/com/android/server/wm/AppCompatUtils.java
index a418324..0369a0f 100644
--- a/services/core/java/com/android/server/wm/AppCompatUtils.java
+++ b/services/core/java/com/android/server/wm/AppCompatUtils.java
@@ -285,7 +285,7 @@
         info.topActivityLetterboxAppWidth = TaskInfo.PROPERTY_VALUE_UNSET;
         info.topActivityLetterboxBounds = null;
         info.cameraCompatTaskInfo.freeformCameraCompatMode =
-                CameraCompatTaskInfo.CAMERA_COMPAT_FREEFORM_NONE;
+                CameraCompatTaskInfo.CAMERA_COMPAT_FREEFORM_UNSPECIFIED;
         info.clearTopActivityFlags();
     }
 }
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index 741eefa..492d84f 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -996,8 +996,7 @@
                 // If the current window container is a task with adjacent task set, the both
                 // adjacent tasks will be opened or closed together. To get their opening or
                 // closing animation target independently, skip promoting their animation targets.
-                if (current.asTask() != null
-                        && current.asTask().getAdjacentTask() != null) {
+                if (current.asTask() != null && current.asTask().hasAdjacentTask()) {
                     canPromote = false;
                 }
 
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index 3968b52..1a7c6b7 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -275,12 +275,8 @@
             final boolean isOccluded = isKeyguardOccluded(window);
             if (!canAnimate) {
                 backType = BackNavigationInfo.TYPE_CALLBACK;
-            } else if ((window.getParent().getChildCount() > 1
-                    && window.getParent().getChildAt(0) != window)) {
-                // TODO Dialog window does not need to attach on activity, check
-                // window.mAttrs.type != TYPE_BASE_APPLICATION
-                // Are we the top window of our parent? If not, we are a window on top of the
-                // activity, we won't close the activity.
+            } else if (window.mAttrs.type != TYPE_BASE_APPLICATION) {
+                // The focus window belongs to an activity and it's not the base window.
                 backType = BackNavigationInfo.TYPE_DIALOG_CLOSE;
                 removedWindowContainer = window;
             } else if (hasTranslucentActivity(currentActivity, prevActivities)) {
@@ -2251,8 +2247,7 @@
         if (w.asTask() != null) {
             final Task task = w.asTask();
             snapshot = task.mRootWindowContainer.mWindowManager.mTaskSnapshotController.getSnapshot(
-                    task.mTaskId, task.mUserId, false /* restoreFromDisk */,
-                    false /* isLowResolution */);
+                    task.mTaskId, false /* isLowResolution */);
         } else {
             ActivityRecord ar = w.asActivityRecord();
             if (ar == null && w.asTaskFragment() != null) {
diff --git a/services/core/java/com/android/server/wm/CameraCompatFreeformPolicy.java b/services/core/java/com/android/server/wm/CameraCompatFreeformPolicy.java
index ae65db4..f5bc9f0 100644
--- a/services/core/java/com/android/server/wm/CameraCompatFreeformPolicy.java
+++ b/services/core/java/com/android/server/wm/CameraCompatFreeformPolicy.java
@@ -21,6 +21,7 @@
 import static android.app.CameraCompatTaskInfo.CAMERA_COMPAT_FREEFORM_NONE;
 import static android.app.CameraCompatTaskInfo.CAMERA_COMPAT_FREEFORM_PORTRAIT_DEVICE_IN_LANDSCAPE;
 import static android.app.CameraCompatTaskInfo.CAMERA_COMPAT_FREEFORM_PORTRAIT_DEVICE_IN_PORTRAIT;
+import static android.app.CameraCompatTaskInfo.CAMERA_COMPAT_FREEFORM_UNSPECIFIED;
 import static android.app.WindowConfiguration.WINDOW_CONFIG_APP_BOUNDS;
 import static android.app.WindowConfiguration.WINDOW_CONFIG_DISPLAY_ROTATION;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LOCKED;
@@ -205,6 +206,16 @@
         }
     }
 
+    /**
+     * Returns true if letterboxing should be allowed for camera apps, even if otherwise it isn't.
+     *
+     * <p>Camera compat is currently the only use-case of letterboxing for desktop windowing.
+     */
+    boolean isFreeformLetterboxingForCameraAllowed(@NonNull ActivityRecord activity) {
+        // Letterboxing is normally not allowed in desktop windowing.
+        return isCameraRunningAndWindowingModeEligible(activity);
+    }
+
     boolean shouldCameraCompatControlOrientation(@NonNull ActivityRecord activity) {
         return isCameraRunningAndWindowingModeEligible(activity);
     }
@@ -225,7 +236,8 @@
     }
 
     boolean isInFreeformCameraCompatMode(@NonNull ActivityRecord activity) {
-        return getCameraCompatMode(activity) != CAMERA_COMPAT_FREEFORM_NONE;
+        return getCameraCompatMode(activity) != CAMERA_COMPAT_FREEFORM_UNSPECIFIED
+                && getCameraCompatMode(activity) != CAMERA_COMPAT_FREEFORM_NONE;
     }
 
     float getCameraCompatAspectRatio(@NonNull ActivityRecord activityRecord) {
diff --git a/services/core/java/com/android/server/wm/ContentRecorder.java b/services/core/java/com/android/server/wm/ContentRecorder.java
index 6ccceb9..8eccffd 100644
--- a/services/core/java/com/android/server/wm/ContentRecorder.java
+++ b/services/core/java/com/android/server/wm/ContentRecorder.java
@@ -473,15 +473,17 @@
             case RECORD_CONTENT_TASK:
                 // Given the WindowToken of the region to record, retrieve the associated
                 // SurfaceControl.
-                if (tokenToRecord == null) {
+                final WindowContainer wc = tokenToRecord != null
+                        ? WindowContainer.fromBinder(tokenToRecord) : null;
+                if (wc == null) {
                     handleStartRecordingFailed();
                     ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
-                            "Content Recording: Unable to start recording due to null token for "
-                                    + "display %d",
+                            "Content Recording: Unable to start recording due to null token or " +
+                                    "null window container for " + "display %d",
                             mDisplayContent.getDisplayId());
                     return null;
                 }
-                Task taskToRecord = WindowContainer.fromBinder(tokenToRecord).asTask();
+                final Task taskToRecord = wc.asTask();
                 if (taskToRecord == null) {
                     handleStartRecordingFailed();
                     ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
diff --git a/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java b/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java
index 37e8f62..4eaa11b 100644
--- a/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java
+++ b/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java
@@ -193,7 +193,7 @@
             final Rect startBounds = new Rect(0, 0, mDisplayContent.mInitialDisplayWidth,
                     mDisplayContent.mInitialDisplayHeight);
             final int fromRotation = mDisplayContent.getRotation();
-            if (Flags.blastSyncNotificationShadeOnDisplaySwitch() && physicalDisplayUpdated) {
+            if (physicalDisplayUpdated) {
                 final WindowState notificationShade =
                         mDisplayContent.getDisplayPolicy().getNotificationShade();
                 if (notificationShade != null && notificationShade.isVisible()
@@ -432,7 +432,8 @@
                 || !first.thermalRefreshRateThrottling.contentEquals(
                 second.thermalRefreshRateThrottling)
                 || !Objects.equals(first.thermalBrightnessThrottlingDataId,
-                second.thermalBrightnessThrottlingDataId)) {
+                second.thermalBrightnessThrottlingDataId)
+                || first.canHostTasks != second.canHostTasks) {
             diff |= DIFF_NOT_WM_DEFERRABLE;
         }
 
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 659bb67..01e00e9 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -2485,7 +2485,7 @@
         final TaskDisplayArea defaultTaskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea();
         final boolean adjacentTasksVisible =
                 defaultTaskDisplayArea.getRootTask(task -> task.isVisible()
-                        && task.getTopLeafTask().getAdjacentTask() != null)
+                        && task.getTopLeafTask().hasAdjacentTask())
                         != null;
         final Task topFreeformTask = defaultTaskDisplayArea
                 .getTopRootTaskInWindowingMode(WINDOWING_MODE_FREEFORM);
diff --git a/services/core/java/com/android/server/wm/InputManagerCallback.java b/services/core/java/com/android/server/wm/InputManagerCallback.java
index 9d21183..7751ac3 100644
--- a/services/core/java/com/android/server/wm/InputManagerCallback.java
+++ b/services/core/java/com/android/server/wm/InputManagerCallback.java
@@ -128,8 +128,7 @@
     @Override
     public void notifyConfigurationChanged() {
         Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "notifyConfigurationChanged");
-        final boolean changed = !com.android.window.flags.Flags.filterIrrelevantInputDeviceChange()
-                || updateLastInputConfigurationSources();
+        final boolean changed = updateLastInputConfigurationSources();
 
         // Even if the input devices are not changed, there could be other pending changes
         // during booting. It's fine to apply earlier.
diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java
index 24a6f118..4bcba13 100644
--- a/services/core/java/com/android/server/wm/InsetsPolicy.java
+++ b/services/core/java/com/android/server/wm/InsetsPolicy.java
@@ -258,16 +258,13 @@
      * We also need to exclude certain types of insets source for client within specific windowing
      * modes.
      *
-     * @param attrs the LayoutParams of the target
-     * @param windowingMode the windowing mode of the target
-     * @param isAlwaysOnTop is the target always on top
+     * @param target the target on which the policy is applied
      * @param state the input inset state containing all the sources
      * @return The state stripped of the necessary information.
      */
-    InsetsState enforceInsetsPolicyForTarget(WindowManager.LayoutParams attrs,
-            @WindowConfiguration.WindowingMode int windowingMode, boolean isAlwaysOnTop,
-            InsetsState state) {
+    InsetsState enforceInsetsPolicyForTarget(WindowState target, InsetsState state) {
         final InsetsState originalState = state;
+        final WindowManager.LayoutParams attrs = target.getAttrs();
 
         // The caller should not receive the visible insets provided by itself.
         if (attrs.type == TYPE_INPUT_METHOD) {
@@ -316,12 +313,17 @@
             }
         }
 
+        final @WindowConfiguration.WindowingMode int windowingMode = target.getWindowingMode();
         if (WindowConfiguration.isFloating(windowingMode)
-                || (windowingMode == WINDOWING_MODE_MULTI_WINDOW && isAlwaysOnTop)) {
+                || (windowingMode == WINDOWING_MODE_MULTI_WINDOW && target.isAlwaysOnTop())) {
             // Keep frames, caption, and IME.
             int types = WindowInsets.Type.captionBar();
             if (windowingMode != WINDOWING_MODE_PINNED) {
-                types |= WindowInsets.Type.ime();
+                if (!Flags.refactorInsetsController() || (mDisplayContent != null
+                        && target == mDisplayContent.getImeInputTarget()
+                        && (WindowInsets.Type.ime() & target.getRequestedVisibleTypes()) != 0)) {
+                    types |= WindowInsets.Type.ime();
+                }
             }
             final InsetsState newState = new InsetsState();
             newState.set(state, types);
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index b3b8c6e..3d28685 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -1796,12 +1796,24 @@
                     activityAssistInfos.clear();
                     activityAssistInfos.add(new ActivityAssistInfo(top));
                     // Check if the activity on the split screen.
-                    final Task adjacentTask = top.getTask().getAdjacentTask();
-                    if (adjacentTask != null) {
-                        final ActivityRecord adjacentActivityRecord =
-                                adjacentTask.getTopNonFinishingActivity();
-                        if (adjacentActivityRecord != null) {
-                            activityAssistInfos.add(new ActivityAssistInfo(adjacentActivityRecord));
+                    if (Flags.allowMultipleAdjacentTaskFragments()) {
+                        top.getTask().forOtherAdjacentTasks(task -> {
+                            final ActivityRecord adjacentActivityRecord =
+                                    task.getTopNonFinishingActivity();
+                            if (adjacentActivityRecord != null) {
+                                activityAssistInfos.add(
+                                        new ActivityAssistInfo(adjacentActivityRecord));
+                            }
+                        });
+                    } else {
+                        final Task adjacentTask = top.getTask().getAdjacentTask();
+                        if (adjacentTask != null) {
+                            final ActivityRecord adjacentActivityRecord =
+                                    adjacentTask.getTopNonFinishingActivity();
+                            if (adjacentActivityRecord != null) {
+                                activityAssistInfos.add(
+                                        new ActivityAssistInfo(adjacentActivityRecord));
+                            }
                         }
                     }
                     if (rootTask == topFocusedRootTask) {
diff --git a/services/core/java/com/android/server/wm/SnapshotCache.java b/services/core/java/com/android/server/wm/SnapshotCache.java
index 1e6ee7d..9812a88 100644
--- a/services/core/java/com/android/server/wm/SnapshotCache.java
+++ b/services/core/java/com/android/server/wm/SnapshotCache.java
@@ -51,7 +51,7 @@
     }
 
     @Nullable
-    final TaskSnapshot getSnapshot(Integer id) {
+    final TaskSnapshot getSnapshotInner(Integer id) {
         synchronized (mLock) {
             // Try the running cache.
             final CacheEntry entry = mRunningCache.get(id);
diff --git a/services/core/java/com/android/server/wm/SnapshotController.java b/services/core/java/com/android/server/wm/SnapshotController.java
index 3ee2e60..dcdffa4 100644
--- a/services/core/java/com/android/server/wm/SnapshotController.java
+++ b/services/core/java/com/android/server/wm/SnapshotController.java
@@ -202,7 +202,7 @@
             final Task task = wc.asTask();
             if (task != null && wc.isVisibleRequested() && !task.inPinnedWindowingMode()) {
                 final TaskSnapshot snapshot = mTaskSnapshotController.getSnapshot(task.mTaskId,
-                        task.mUserId, false /* restoreFromDisk */, false /* isLowResolution */);
+                        false /* isLowResolution */);
                 if (snapshot != null) {
                     mTaskSnapshotController.removeAndDeleteSnapshot(task.mTaskId, task.mUserId);
                 }
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index c8befb2..76d8861 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -2500,20 +2500,79 @@
         return parentTask == null ? null : parentTask.getCreatedByOrganizerTask();
     }
 
-    /** @return the first adjacent task of this task or its parent. */
+    /** @deprecated b/373709676 replace with {@link #forOtherAdjacentTasks(Consumer)} ()}. */
+    @Deprecated
     @Nullable
     Task getAdjacentTask() {
-        final TaskFragment adjacentTaskFragment = getAdjacentTaskFragment();
-        if (adjacentTaskFragment != null && adjacentTaskFragment.asTask() != null) {
-            return adjacentTaskFragment.asTask();
+        if (Flags.allowMultipleAdjacentTaskFragments()) {
+            throw new IllegalStateException("allowMultipleAdjacentTaskFragments is enabled. "
+                    + "Use #forOtherAdjacentTasks instead");
         }
+        final Task taskWithAdjacent = getTaskWithAdjacent();
+        if (taskWithAdjacent == null) {
+            return null;
+        }
+        return taskWithAdjacent.getAdjacentTaskFragment().asTask();
+    }
 
+    /** Finds the first Task parent (or itself) that has adjacent. */
+    @Nullable
+    Task getTaskWithAdjacent() {
+        if (hasAdjacentTaskFragment()) {
+            return this;
+        }
         final WindowContainer parent = getParent();
         if (parent == null || parent.asTask() == null) {
             return null;
         }
+        return parent.asTask().getTaskWithAdjacent();
+    }
 
-        return parent.asTask().getAdjacentTask();
+    /** Returns true if this or its parent has adjacent Task. */
+    boolean hasAdjacentTask() {
+        return getTaskWithAdjacent() != null;
+    }
+
+    /**
+     * Finds the first Task parent (or itself) that has adjacent. Runs callback on all the adjacent
+     * Tasks. The invoke order is not guaranteed.
+     */
+    void forOtherAdjacentTasks(@NonNull Consumer<Task> callback) {
+        if (!Flags.allowMultipleAdjacentTaskFragments()) {
+            throw new IllegalStateException("allowMultipleAdjacentTaskFragments is not enabled. "
+                    + "Use #getAdjacentTask instead");
+        }
+
+        final Task taskWithAdjacent = getTaskWithAdjacent();
+        if (taskWithAdjacent == null) {
+            return;
+        }
+        final AdjacentSet adjacentTasks = taskWithAdjacent.getAdjacentTaskFragments();
+        adjacentTasks.forAllTaskFragments(tf -> {
+            // We don't support Task adjacent to non-Task TaskFragment.
+            callback.accept(tf.asTask());
+        }, taskWithAdjacent /* exclude */);
+    }
+
+    /**
+     * Finds the first Task parent (or itself) that has adjacent. Runs callback on all the adjacent
+     * Tasks. Returns early if callback returns true on any of them. The invoke order is not
+     * guaranteed.
+     */
+    boolean forOtherAdjacentTasks(@NonNull Predicate<Task> callback) {
+        if (!Flags.allowMultipleAdjacentTaskFragments()) {
+            throw new IllegalStateException("allowMultipleAdjacentTaskFragments is not enabled. "
+                    + "Use getAdjacentTask instead");
+        }
+        final Task taskWithAdjacent = getTaskWithAdjacent();
+        if (taskWithAdjacent == null) {
+            return false;
+        }
+        final AdjacentSet adjacentTasks = taskWithAdjacent.getAdjacentTaskFragments();
+        return adjacentTasks.forAllTaskFragments(tf -> {
+            // We don't support Task adjacent to non-Task TaskFragment.
+            return callback.test(tf.asTask());
+        }, taskWithAdjacent /* exclude */);
     }
 
     // TODO(task-merge): Figure out what's the right thing to do for places that used it.
@@ -2907,7 +2966,7 @@
             Rect outSurfaceInsets) {
         // If this task has its adjacent task, it means they should animate together. Use display
         // bounds for them could move same as full screen task.
-        if (getAdjacentTask() != null) {
+        if (hasAdjacentTask()) {
             super.getAnimationFrames(outFrame, outInsets, outStableInsets, outSurfaceInsets);
             return;
         }
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index c1a33c4..9564c59 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -61,6 +61,7 @@
 import com.android.internal.util.function.pooled.PooledPredicate;
 import com.android.server.pm.UserManagerInternal;
 import com.android.server.wm.LaunchParamsController.LaunchParams;
+import com.android.window.flags.Flags;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -1088,8 +1089,19 @@
         // Use launch-adjacent-flag-root if launching with launch-adjacent flag.
         if ((launchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) != 0
                 && mLaunchAdjacentFlagRootTask != null) {
-            final Task launchAdjacentRootAdjacentTask =
-                    mLaunchAdjacentFlagRootTask.getAdjacentTask();
+            final Task launchAdjacentRootAdjacentTask;
+            if (Flags.allowMultipleAdjacentTaskFragments()) {
+                final Task[] tmpTask = new Task[1];
+                mLaunchAdjacentFlagRootTask.forOtherAdjacentTasks(task -> {
+                    // TODO(b/382208145): enable FLAG_ACTIVITY_LAUNCH_ADJACENT for 3+.
+                    // Find the first adjacent for now.
+                    tmpTask[0] = task;
+                    return true;
+                });
+                launchAdjacentRootAdjacentTask = tmpTask[0];
+            } else {
+                launchAdjacentRootAdjacentTask = mLaunchAdjacentFlagRootTask.getAdjacentTask();
+            }
             if (sourceTask != null && (sourceTask == candidateTask
                     || sourceTask.topRunningActivity() == null)) {
                 // Do nothing when task that is getting opened is same as the source or when
@@ -1114,15 +1126,26 @@
         for (int i = mLaunchRootTasks.size() - 1; i >= 0; --i) {
             if (mLaunchRootTasks.get(i).contains(windowingMode, activityType)) {
                 final Task launchRootTask = mLaunchRootTasks.get(i).task;
-                final Task adjacentRootTask = launchRootTask != null
-                        ? launchRootTask.getAdjacentTask() : null;
-                if (sourceTask != null && adjacentRootTask != null
-                        && (sourceTask == adjacentRootTask
-                        || sourceTask.isDescendantOf(adjacentRootTask))) {
-                    return adjacentRootTask;
-                } else {
+                if (launchRootTask == null || sourceTask == null) {
                     return launchRootTask;
                 }
+                if (!Flags.allowMultipleAdjacentTaskFragments()) {
+                    final Task adjacentRootTask = launchRootTask.getAdjacentTask();
+                    if (adjacentRootTask != null && (sourceTask == adjacentRootTask
+                            || sourceTask.isDescendantOf(adjacentRootTask))) {
+                        return adjacentRootTask;
+                    }
+                    return launchRootTask;
+                }
+                final Task[] adjacentRootTask = new Task[1];
+                launchRootTask.forOtherAdjacentTasks(task -> {
+                    if (sourceTask == task || sourceTask.isDescendantOf(task)) {
+                        adjacentRootTask[0] = task;
+                        return true;
+                    }
+                    return false;
+                });
+                return adjacentRootTask[0] != null ? adjacentRootTask[0] : launchRootTask;
             }
         }
 
@@ -1133,12 +1156,31 @@
                 // A pinned task relaunching should be handled by its task organizer. Skip fallback
                 // launch target of a pinned task from source task.
                 || candidateTask.getWindowingMode() != WINDOWING_MODE_PINNED)) {
-            final Task adjacentTarget = sourceTask.getAdjacentTask();
-            if (adjacentTarget != null) {
-                if (candidateTask != null
-                        && (candidateTask == adjacentTarget
-                        || candidateTask.isDescendantOf(adjacentTarget))) {
-                    return adjacentTarget;
+            final Task taskWithAdjacent = sourceTask.getTaskWithAdjacent();
+            if (taskWithAdjacent != null) {
+                // Has adjacent.
+                if (candidateTask == null) {
+                    return sourceTask.getCreatedByOrganizerTask();
+                }
+                // Check if the candidate is already positioned in the adjacent Task.
+                if (Flags.allowMultipleAdjacentTaskFragments()) {
+                    final Task[] adjacentRootTask = new Task[1];
+                    sourceTask.forOtherAdjacentTasks(task -> {
+                        if (candidateTask == task || candidateTask.isDescendantOf(task)) {
+                            adjacentRootTask[0] = task;
+                            return true;
+                        }
+                        return false;
+                    });
+                    if (adjacentRootTask[0] != null) {
+                        return adjacentRootTask[0];
+                    }
+                } else {
+                    final Task adjacentTarget = taskWithAdjacent.getAdjacentTask();
+                    if (candidateTask == adjacentTarget
+                            || candidateTask.isDescendantOf(adjacentTarget)) {
+                        return adjacentTarget;
+                    }
                 }
                 return sourceTask.getCreatedByOrganizerTask();
             }
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 9fb5bea..cb6b690 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -531,6 +531,28 @@
         return mAdjacentTaskFragments;
     }
 
+    /**
+     * Runs callback on all TaskFragments that are adjacent to this. The invoke order is not
+     * guaranteed.
+     */
+    void forOtherAdjacentTaskFragments(@NonNull Consumer<TaskFragment> callback) {
+        if (mAdjacentTaskFragments == null) {
+            return;
+        }
+        mAdjacentTaskFragments.forAllTaskFragments(callback, this /* exclude */);
+    }
+
+    /**
+     * Runs callback on all TaskFragments that are adjacent to this. Returns early if callback
+     * returns true on any of them. The invoke order is not guaranteed.
+     */
+    boolean forOtherAdjacentTaskFragments(@NonNull Predicate<TaskFragment> callback) {
+        if (mAdjacentTaskFragments == null) {
+            return false;
+        }
+        return mAdjacentTaskFragments.forAllTaskFragments(callback, this /* exclude */);
+    }
+
     boolean hasAdjacentTaskFragment() {
         if (!Flags.allowMultipleAdjacentTaskFragments()) {
             return mAdjacentTaskFragment != null;
@@ -3198,24 +3220,47 @@
             return false;
         }
 
-        final TaskFragment adjacentTf = getAdjacentTaskFragment();
-        if (adjacentTf == null) {
+        if (!hasAdjacentTaskFragment()) {
             // early return if no adjacent TF.
             return false;
         }
 
-        if (getParent().mChildren.indexOf(adjacentTf) < getParent().mChildren.indexOf(this)) {
-            // early return if this TF already has higher z-ordering.
-            return false;
+        final ArrayList<WindowContainer> siblings = getParent().mChildren;
+        final int zOrder = siblings.indexOf(this);
+
+        if (!Flags.allowMultipleAdjacentTaskFragments()) {
+            if (siblings.indexOf(getAdjacentTaskFragment()) < zOrder) {
+                // early return if this TF already has higher z-ordering.
+                return false;
+            }
+        } else {
+            final boolean hasAdjacentOnTop = forOtherAdjacentTaskFragments(
+                    tf -> siblings.indexOf(tf) > zOrder);
+            if (!hasAdjacentOnTop) {
+                // early return if this TF already has higher z-ordering.
+                return false;
+            }
         }
 
-        ToBooleanFunction<WindowState> getDimBehindWindow =
+        final ToBooleanFunction<WindowState> getDimBehindWindow =
                 (w) -> (w.mAttrs.flags & FLAG_DIM_BEHIND) != 0 && w.mActivityRecord != null
                         && w.mActivityRecord.isEmbedded() && (w.mActivityRecord.isVisibleRequested()
                         || w.mActivityRecord.isVisible());
-        if (adjacentTf.forAllWindows(getDimBehindWindow, true)) {
-            // early return if the adjacent Tf has a dimming window.
-            return false;
+
+        if (!Flags.allowMultipleAdjacentTaskFragments()) {
+            final TaskFragment adjacentTf = getAdjacentTaskFragment();
+            if (adjacentTf.forAllWindows(getDimBehindWindow, true)) {
+                // early return if the adjacent Tf has a dimming window.
+                return false;
+            }
+        } else {
+            final boolean adjacentHasDimmingWindow = forOtherAdjacentTaskFragments(tf -> {
+                return tf.forAllWindows(getDimBehindWindow, true);
+            });
+            if (adjacentHasDimmingWindow) {
+                // early return if the adjacent Tf has a dimming window.
+                return false;
+            }
         }
 
         // boost if there's an Activity window that has FLAG_DIM_BEHIND flag.
@@ -3528,6 +3573,37 @@
             return mAdjacentSet.contains(taskFragment);
         }
 
+        /**
+         * Runs the callback on all adjacent TaskFragments. Skips the exclude one if not null.
+         */
+        void forAllTaskFragments(@NonNull Consumer<TaskFragment> callback,
+                @Nullable TaskFragment exclude) {
+            for (int i = mAdjacentSet.size() - 1; i >= 0; i--) {
+                final TaskFragment taskFragment = mAdjacentSet.valueAt(i);
+                if (taskFragment != exclude) {
+                    callback.accept(taskFragment);
+                }
+            }
+        }
+
+        /**
+         * Runs the callback on all adjacent TaskFragments until one returns {@code true}. Skips the
+         * exclude one if not null.
+         */
+        boolean forAllTaskFragments(@NonNull Predicate<TaskFragment> callback,
+                @Nullable TaskFragment exclude) {
+            for (int i = mAdjacentSet.size() - 1; i >= 0; i--) {
+                final TaskFragment taskFragment = mAdjacentSet.valueAt(i);
+                if (taskFragment == exclude) {
+                    continue;
+                }
+                if (callback.test(taskFragment)) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
         @Override
         public boolean equals(@Nullable Object o) {
             if (this == o) {
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotCache.java b/services/core/java/com/android/server/wm/TaskSnapshotCache.java
index 64b9df5..cc957bd 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotCache.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotCache.java
@@ -48,26 +48,37 @@
     }
 
     /**
-     * If {@param restoreFromDisk} equals {@code true}, DO NOT HOLD THE WINDOW MANAGER LOCK!
+     * Retrieves a snapshot from cache.
      */
-    @Nullable TaskSnapshot getSnapshot(int taskId, int userId, boolean restoreFromDisk,
-            boolean isLowResolution) {
-        final TaskSnapshot snapshot = getSnapshot(taskId);
-        if (snapshot != null) {
-            return snapshot;
-        }
+    @Nullable TaskSnapshot getSnapshot(int taskId, boolean isLowResolution) {
+        return getSnapshot(taskId, isLowResolution, TaskSnapshot.REFERENCE_NONE);
+    }
 
-        // Try to restore from disk if asked.
-        if (!restoreFromDisk) {
-            return null;
+    // TODO (b/238206323) Respect isLowResolution.
+    @Nullable TaskSnapshot getSnapshot(int taskId, boolean isLowResolution,
+            @TaskSnapshot.ReferenceFlags int usage) {
+        synchronized (mLock) {
+            final TaskSnapshot snapshot = getSnapshotInner(taskId);
+            if (snapshot != null) {
+                if (usage != TaskSnapshot.REFERENCE_NONE) {
+                    snapshot.addReference(usage);
+                }
+                return snapshot;
+            }
         }
-        return tryRestoreFromDisk(taskId, userId, isLowResolution);
+        return null;
     }
 
     /**
-     * DO NOT HOLD THE WINDOW MANAGER LOCK WHEN CALLING THIS METHOD!
+     * Restore snapshot from disk, DO NOT HOLD THE WINDOW MANAGER LOCK!
      */
-    private TaskSnapshot tryRestoreFromDisk(int taskId, int userId, boolean isLowResolution) {
-        return mLoader.loadTask(taskId, userId, isLowResolution);
+    @Nullable TaskSnapshot getSnapshotFromDisk(int taskId, int userId, boolean isLowResolution,
+            @TaskSnapshot.ReferenceFlags int usage) {
+        final TaskSnapshot snapshot = mLoader.loadTask(taskId, userId, isLowResolution);
+        // Note: This can be weird if the caller didn't ask for reference.
+        if (snapshot != null && usage != TaskSnapshot.REFERENCE_NONE) {
+            snapshot.addReference(usage);
+        }
+        return snapshot;
     }
 }
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index c130931..38a2ebe 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -174,14 +174,32 @@
     }
 
     /**
-     * Retrieves a snapshot. If {@param restoreFromDisk} equals {@code true}, DO NOT HOLD THE WINDOW
-     * MANAGER LOCK WHEN CALLING THIS METHOD!
+     * Retrieves a snapshot from cache.
      */
     @Nullable
-    TaskSnapshot getSnapshot(int taskId, int userId, boolean restoreFromDisk,
-            boolean isLowResolution) {
-        return mCache.getSnapshot(taskId, userId, restoreFromDisk, isLowResolution
-                && mPersistInfoProvider.enableLowResSnapshots());
+    TaskSnapshot getSnapshot(int taskId, boolean isLowResolution) {
+        return getSnapshot(taskId, false /* isLowResolution */, TaskSnapshot.REFERENCE_NONE);
+    }
+
+    /**
+     * Retrieves a snapshot from cache.
+     */
+    @Nullable
+    TaskSnapshot getSnapshot(int taskId, boolean isLowResolution,
+            @TaskSnapshot.ReferenceFlags int usage) {
+        return mCache.getSnapshot(taskId, isLowResolution
+                && mPersistInfoProvider.enableLowResSnapshots(), usage);
+    }
+
+    /**
+     * Retrieves a snapshot from disk.
+     * DO NOT HOLD THE WINDOW MANAGER LOCK WHEN CALLING THIS METHOD!
+     */
+    @Nullable
+    TaskSnapshot getSnapshotFromDisk(int taskId, int userId,
+            boolean isLowResolution, @TaskSnapshot.ReferenceFlags int usage) {
+        return mCache.getSnapshotFromDisk(taskId, userId, isLowResolution
+                && mPersistInfoProvider.enableLowResSnapshots(), usage);
     }
 
     /**
@@ -189,7 +207,7 @@
      * last taken, or -1 if no such snapshot exists for that task.
      */
     long getSnapshotCaptureTime(int taskId) {
-        final TaskSnapshot snapshot = mCache.getSnapshot(taskId);
+        final TaskSnapshot snapshot = mCache.getSnapshot(taskId, false /* isLowResolution */);
         if (snapshot != null) {
             return snapshot.getCaptureTime();
         }
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 1fc609b7..1f539a1 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -1439,7 +1439,7 @@
                     }
                 }
             }
-            if (wt == null) continue;
+            if (wt == null || !wt.isVisible()) continue;
             final WindowState target = wt.mDisplayContent.mWallpaperController.getWallpaperTarget();
             final boolean isTargetInvisible = target == null || !target.mToken.isVisible();
             final boolean isWallpaperVisibleAtEnd =
@@ -1870,7 +1870,13 @@
             final DisplayArea<?> da = wc.asDisplayArea();
             if (da == null) continue;
             if (da.isVisibleRequested()) {
-                mController.mValidateDisplayVis.remove(da);
+                final int inValidateList = mController.mValidateDisplayVis.indexOf(da);
+                if (inValidateList >= 0
+                        // The display-area is visible, but if we only detect a non-visibility
+                        // change, then we shouldn't remove the validator.
+                        && !mChanges.get(da).mVisible) {
+                    mController.mValidateDisplayVis.remove(inValidateList);
+                }
             } else {
                 // In case something accidentally hides a displayarea and nothing shows it again.
                 mController.mValidateDisplayVis.add(da);
@@ -2271,19 +2277,6 @@
         }
     }
 
-    /**
-
-     * Wallpaper will set itself as target if it wants to keep itself visible without a target.
-     */
-    private static boolean wallpaperIsOwnTarget(WallpaperWindowToken wallpaper) {
-        final WindowState target =
-                wallpaper.getDisplayContent().mWallpaperController.getWallpaperTarget();
-        return target != null && target.isDescendantOf(wallpaper);
-    }
-
-    /**
-     * Reset waitingToshow for all wallpapers, and commit the visibility of the visible ones
-     */
     private void commitVisibleWallpapers(SurfaceControl.Transaction t) {
         boolean showWallpaper = shouldWallpaperBeVisible();
         for (int i = mParticipants.size() - 1; i >= 0; --i) {
@@ -2291,11 +2284,6 @@
             if (wallpaper != null) {
                 if (!wallpaper.isVisible() && wallpaper.isVisibleRequested()) {
                     wallpaper.commitVisibility(showWallpaper);
-                } else if (wallpaper.mWmService.mFlags.mEnsureWallpaperInTransitions
-                        && wallpaper.isVisible()
-                        && !showWallpaper && !wallpaper.getDisplayContent().isKeyguardLocked()
-                        && !wallpaperIsOwnTarget(wallpaper)) {
-                    wallpaper.setVisibleRequested(false);
                 }
                 if (showWallpaper && wallpaper.isVisibleRequested()) {
                     for (int j = wallpaper.mChildren.size() - 1; j >= 0; --j) {
@@ -4144,7 +4132,9 @@
                             .setSourceCrop(cropBounds)
                             .setCaptureSecureLayers(true)
                             .setAllowProtected(true)
-                            .setHintForSeamlessTransition(isDisplayRotation)
+                            // We always reroute this screenshot to the display, so this transition
+                            // is ALWAYS seamless
+                            .setHintForSeamlessTransition(true)
                             .build();
             ScreenCapture.ScreenshotHardwareBuffer screenshotBuffer =
                     ScreenCapture.captureLayers(captureArgs);
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index 7565b5d..c1ef208 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -895,7 +895,11 @@
             }
         }
 
-        updateWallpaperTokens(visible, mDisplayContent.isKeyguardLocked());
+        boolean visibleRequested = visible;
+        if (mDisplayContent.mWmService.mFlags.mEnsureWallpaperInTransitions) {
+            visibleRequested = mWallpaperTarget != null && mWallpaperTarget.isVisibleRequested();
+        }
+        updateWallpaperTokens(visibleRequested, mDisplayContent.isKeyguardLocked());
 
         ProtoLog.v(WM_DEBUG_WALLPAPER,
                 "Wallpaper at display %d - visibility: %b, keyguardLocked: %b",
@@ -1104,7 +1108,6 @@
         for (int i = mWallpaperTokens.size() - 1; i >= 0; i--) {
             final WallpaperWindowToken t = mWallpaperTokens.get(i);
             pw.print(prefix); pw.println("token " + t + ":");
-            pw.print(prefix); pw.print("  canShowWhenLocked="); pw.println(t.canShowWhenLocked());
             dumpValue(pw, prefix, "mWallpaperX", t.mWallpaperX);
             dumpValue(pw, prefix, "mWallpaperY", t.mWallpaperY);
             dumpValue(pw, prefix, "mWallpaperXStep", t.mWallpaperXStep);
diff --git a/services/core/java/com/android/server/wm/WallpaperWindowToken.java b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
index 2c926fc..0ecd025 100644
--- a/services/core/java/com/android/server/wm/WallpaperWindowToken.java
+++ b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
@@ -33,6 +33,7 @@
 
 import com.android.internal.protolog.ProtoLog;
 
+import java.io.PrintWriter;
 import java.util.function.Consumer;
 
 /**
@@ -103,6 +104,7 @@
             return;
         }
         mShowWhenLocked = showWhenLocked;
+        stringName = null;
         // Move the window token to the front (private) or back (showWhenLocked). This is possible
         // because the DisplayArea underneath TaskDisplayArea only contains TYPE_WALLPAPER windows.
         final int position = showWhenLocked ? POSITION_BOTTOM : POSITION_TOP;
@@ -286,13 +288,18 @@
     }
 
     @Override
+    void dump(PrintWriter pw, String prefix, boolean dumpAll) {
+        super.dump(pw, prefix, dumpAll);
+        pw.print(prefix); pw.print("visibleRequested="); pw.print(mVisibleRequested);
+        pw.print(" visible="); pw.println(isVisible());
+    }
+
+    @Override
     public String toString() {
         if (stringName == null) {
-            StringBuilder sb = new StringBuilder();
-            sb.append("WallpaperWindowToken{");
-            sb.append(Integer.toHexString(System.identityHashCode(this)));
-            sb.append(" token="); sb.append(token); sb.append('}');
-            stringName = sb.toString();
+            stringName = "WallpaperWindowToken{"
+                    + Integer.toHexString(System.identityHashCode(this))
+                    + " showWhenLocked=" + mShowWhenLocked + '}';
         }
         return stringName;
     }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 58319f4..9d9c53d 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -4292,16 +4292,6 @@
     }
 
     /**
-     * Retrieves a snapshot. If restoreFromDisk equals equals {@code true}, DO NOT HOLD THE WINDOW
-     * MANAGER LOCK WHEN CALLING THIS METHOD!
-     */
-    public TaskSnapshot getTaskSnapshot(int taskId, int userId, boolean isLowResolution,
-            boolean restoreFromDisk) {
-        return mTaskSnapshotController.getSnapshot(taskId, userId, restoreFromDisk,
-                isLowResolution);
-    }
-
-    /**
      * Generates and returns an up-to-date {@link Bitmap} for the specified taskId.
      *
      * @param taskId                  The task ID of the task for which a Bitmap is requested.
@@ -9142,9 +9132,13 @@
 
     private void handlePointerDownOutsideFocus(InputTarget t, InputTarget focusedInputTarget) {
         synchronized (mGlobalLock) {
-            if (mFocusedInputTarget != focusedInputTarget) {
-                // Skip if the mFocusedInputTarget is already changed. This is possible if the
-                // pointer-down-outside-focus event is delayed to be handled.
+            final WindowState w = t.getWindowState();
+            // Skip if the mFocusedInputTarget is already changed or the touched Activity is no
+            // longer visible. This is possible if the pointer-down-outside-focus event is
+            // delayed to be handled.
+            if (mFocusedInputTarget != focusedInputTarget || (w != null
+                    && w.getActivityRecord() != null
+                    && !w.getActivityRecord().isVisibleRequested())) {
                 ProtoLog.i(WM_DEBUG_FOCUS_LIGHT,
                         "Skip onPointerDownOutsideFocusLocked due to input target changed %s", t);
                 return;
@@ -9156,7 +9150,6 @@
             }
             clearPointerDownOutsideFocusRunnable();
 
-            final WindowState w = t.getWindowState();
             if (w != null) {
                 final Task task = w.getTask();
                 if (task != null && w.mTransitionController.isTransientHide(task)) {
@@ -10021,11 +10014,10 @@
                     && imeTargetWindow.mActivityRecord.mLastImeShown) {
                 return true;
             }
+            final TaskSnapshot snapshot = mTaskSnapshotController.getSnapshot(
+                    imeTargetWindowTask.mTaskId, false /* isLowResolution */);
+            return snapshot != null && snapshot.hasImeSurface();
         }
-        final TaskSnapshot snapshot = getTaskSnapshot(imeTargetWindowTask.mTaskId,
-                imeTargetWindowTask.mUserId, false /* isLowResolution */,
-                false /* restoreFromDisk */);
-        return snapshot != null && snapshot.hasImeSurface();
     }
 
     @Override
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 7f0c336..80e4c30 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -1263,7 +1263,7 @@
                     if (windowingMode == WINDOWING_MODE_MULTI_WINDOW
                             && com.android.window.flags.Flags
                                     .processPriorityPolicyForMultiWindowMode()
-                            && task.getAdjacentTask() != null) {
+                            && task.hasAdjacentTask()) {
                         stateFlags |= ACTIVITY_STATE_FLAG_RESUMED_SPLIT_SCREEN;
                     } else if (windowingMode == WINDOWING_MODE_FREEFORM) {
                         hasResumedFreeform = true;
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index cebe790..68b4b6f 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1631,8 +1631,8 @@
         }
         final InsetsState rawInsetsState =
                 mFrozenInsetsState != null ? mFrozenInsetsState : getMergedInsetsState();
-        final InsetsState insetsStateForWindow = insetsPolicy.enforceInsetsPolicyForTarget(
-                mAttrs, getWindowingMode(), isAlwaysOnTop(), rawInsetsState);
+        final InsetsState insetsStateForWindow = insetsPolicy.enforceInsetsPolicyForTarget(this,
+                rawInsetsState);
         return insetsPolicy.adjustInsetsForWindow(this, insetsStateForWindow,
                 includeTransient);
     }
@@ -3303,7 +3303,8 @@
             // just kill it. And if it is a window of foreground activity, the activity can be
             // restarted automatically if needed.
             Slog.w(TAG, "Exception thrown during dispatchAppVisibility " + this, e);
-            if (android.os.Process.getUidForPid(mSession.mPid) == mSession.mUid) {
+            if (android.os.Process.getUidForPid(mSession.mPid) == mSession.mUid
+                    && android.os.Process.getThreadGroupLeader(mSession.mPid) == mSession.mPid) {
                 android.os.Process.killProcess(mSession.mPid);
             }
         }
@@ -3311,8 +3312,7 @@
         // Because the client is notified to be invisible, it should no longer be considered as
         // drawn state. This prevent the app from showing incomplete content if the app is
         // requested to be visible in a short time (e.g. before activity stopped).
-        if (Flags.resetDrawStateOnClientInvisible() && !clientVisible && mActivityRecord != null
-                && mWinAnimator.mDrawState == HAS_DRAWN) {
+        if (!clientVisible && mActivityRecord != null && mWinAnimator.mDrawState == HAS_DRAWN) {
             mWinAnimator.resetDrawState();
             // Make sure the app can report drawn if it becomes visible again.
             forceReportingResized();
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 4c0cee4..01639cc 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -11,7 +11,6 @@
     name: "libservices.core",
     defaults: ["libservices.core-libs"],
 
-    cpp_std: "c++2a",
     cflags: [
         "-Wall",
         "-Werror",
@@ -62,6 +61,7 @@
         "com_android_server_SystemServer.cpp",
         "com_android_server_tv_TvUinputBridge.cpp",
         "com_android_server_tv_TvInputHal.cpp",
+        "com_android_server_UsbAlsaDevice.cpp",
         "com_android_server_UsbAlsaJackDetector.cpp",
         "com_android_server_UsbAlsaMidiDevice.cpp",
         "com_android_server_UsbDeviceManager.cpp",
diff --git a/services/core/jni/com_android_server_UsbAlsaDevice.cpp b/services/core/jni/com_android_server_UsbAlsaDevice.cpp
new file mode 100644
index 0000000..166932f
--- /dev/null
+++ b/services/core/jni/com_android_server_UsbAlsaDevice.cpp
@@ -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.
+ */
+
+#define LOG_TAG "UsbAlsaDeviceJNI"
+
+#include <nativehelper/JNIPlatformHelp.h>
+#include <tinyalsa/asoundlib.h>
+
+#include <string>
+#include <vector>
+
+#include "jni.h"
+#include "utils/Log.h"
+
+static const std::vector<std::string> POSSIBLE_HARDWARE_VOLUME_MIXER_NAMES =
+        {"Headphone Playback Volume", "Headset Playback Volume", "PCM Playback Volume"};
+
+namespace android {
+
+static void android_server_UsbAlsaDevice_setVolume(JNIEnv* /*env*/, jobject /*thiz*/, jint card,
+                                                   float volume) {
+    ALOGD("%s(%d, %f)", __func__, card, volume);
+    struct mixer* alsaMixer = mixer_open(card);
+    if (alsaMixer == nullptr) {
+        ALOGW("%s(%d, %f) returned as no mixer is opened", __func__, card, volume);
+        return;
+    }
+    struct mixer_ctl* ctl = nullptr;
+    for (const auto& mixerName : POSSIBLE_HARDWARE_VOLUME_MIXER_NAMES) {
+        ctl = mixer_get_ctl_by_name(alsaMixer, mixerName.c_str());
+        if (ctl != nullptr) {
+            break;
+        }
+    }
+    if (ctl == nullptr) {
+        ALOGW("%s(%d, %f) returned as no volume mixer is found", __func__, card, volume);
+        return;
+    }
+    const unsigned int n = mixer_ctl_get_num_values(ctl);
+    for (unsigned int id = 0; id < n; id++) {
+        if (int error = mixer_ctl_set_percent(ctl, id, 100 * volume); error != 0) {
+            ALOGE("%s(%d, %f) failed, error=%d", __func__, card, volume, error);
+            return;
+        }
+    }
+    ALOGD("%s(%d, %f) succeed", __func__, card, volume);
+}
+
+static JNINativeMethod method_table[] = {
+        {"nativeSetVolume", "(IF)V", (void*)android_server_UsbAlsaDevice_setVolume},
+};
+
+int register_android_server_UsbAlsaDevice(JNIEnv* env) {
+    return jniRegisterNativeMethods(env, "com/android/server/usb/UsbAlsaDevice", method_table,
+                                    NELEM(method_table));
+}
+} // namespace android
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index 09fd8d4..e3bd69c 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -33,6 +33,7 @@
 int register_android_server_HintManagerService(JNIEnv* env);
 int register_android_server_storage_AppFuse(JNIEnv* env);
 int register_android_server_SystemServer(JNIEnv* env);
+int register_android_server_UsbAlsaDevice(JNIEnv* env);
 int register_android_server_UsbAlsaJackDetector(JNIEnv* env);
 int register_android_server_UsbAlsaMidiDevice(JNIEnv* env);
 int register_android_server_UsbDeviceManager(JavaVM* vm, JNIEnv* env);
@@ -98,6 +99,7 @@
     register_android_server_InputManager(env);
     register_android_server_LightsService(env);
     register_android_server_UsbDeviceManager(vm, env);
+    register_android_server_UsbAlsaDevice(env);
     register_android_server_UsbAlsaJackDetector(env);
     register_android_server_UsbAlsaMidiDevice(env);
     register_android_server_UsbHostManager(env);
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 92dbf63..60130d1 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -109,6 +109,7 @@
 import com.android.internal.os.ApplicationSharedMemory;
 import com.android.internal.os.BinderInternal;
 import com.android.internal.os.RuntimeInit;
+import com.android.internal.os.logging.MetricsLoggerWrapper;
 import com.android.internal.pm.RoSystemFeatures;
 import com.android.internal.policy.AttributeCache;
 import com.android.internal.protolog.ProtoLog;
@@ -293,6 +294,7 @@
 import com.android.server.usage.UsageStatsService;
 import com.android.server.usb.UsbService;
 import com.android.server.utils.TimingsTraceAndSlog;
+import com.android.server.vcn.VcnLocation;
 import com.android.server.vibrator.VibratorManagerService;
 import com.android.server.voiceinteraction.VoiceInteractionManagerService;
 import com.android.server.vr.VrManagerService;
@@ -1001,6 +1003,17 @@
             }
         });
 
+        // Register callback to report native memory metrics post GC cleanup
+        // for system_server
+        if (android.app.Flags.reportPostgcMemoryMetrics() &&
+            com.android.libcore.readonly.Flags.postCleanupApis()) {
+            VMRuntime.addPostCleanupCallback(new Runnable() {
+                @Override public void run() {
+                    MetricsLoggerWrapper.logPostGcMemorySnapshot();
+                }
+            });
+        }
+
         // Loop forever.
         Looper.loop();
         throw new RuntimeException("Main thread loop unexpectedly exited");
@@ -1929,6 +1942,10 @@
         }
         t.traceEnd();
 
+        t.traceBegin("UpdateMetricsIfNeeded");
+        mPackageManagerService.updateMetricsIfNeeded();
+        t.traceEnd();
+
         t.traceBegin("PerformFstrimIfNeeded");
         try {
             mPackageManagerService.performFstrimIfNeeded();
@@ -2150,13 +2167,14 @@
                 mSystemServiceManager.startServiceFromJar(
                         WIFI_SCANNING_SERVICE_CLASS, WIFI_APEX_SERVICE_JAR_PATH);
                 t.traceEnd();
-                // Start USD service
-                if (android.net.wifi.flags.Flags.usd()) {
-                    t.traceBegin("StartUsd");
-                    mSystemServiceManager.startServiceFromJar(
-                            WIFI_USD_SERVICE_CLASS, WIFI_APEX_SERVICE_JAR_PATH);
-                    t.traceEnd();
-                }
+            }
+
+            if (android.net.wifi.flags.Flags.usd() && context.getResources().getBoolean(
+                    com.android.internal.R.bool.config_deviceSupportsWifiUsd)) {
+                t.traceBegin("StartWifiUsd");
+                mSystemServiceManager.startServiceFromJar(WIFI_USD_SERVICE_CLASS,
+                        WIFI_APEX_SERVICE_JAR_PATH);
+                t.traceEnd();
             }
 
             if (context.getPackageManager().hasSystemFeature(
@@ -2234,10 +2252,13 @@
 
             t.traceBegin("StartVcnManagementService");
             try {
-                // TODO: b/375213246 When VCN is in mainline module, load it from the apex path.
-                // Whether VCN will be in apex or in the platform will be gated by a build system
-                // flag.
-                mSystemServiceManager.startService(CONNECTIVITY_SERVICE_INITIALIZER_B_CLASS);
+                if (VcnLocation.IS_VCN_IN_MAINLINE) {
+                    mSystemServiceManager.startServiceFromJar(
+                            CONNECTIVITY_SERVICE_INITIALIZER_B_CLASS,
+                            CONNECTIVITY_SERVICE_APEX_PATH);
+                } else {
+                    mSystemServiceManager.startService(CONNECTIVITY_SERVICE_INITIALIZER_B_CLASS);
+                }
             } catch (Throwable e) {
                 reportWtf("starting VCN Management Service", e);
             }
diff --git a/services/manifest_services.xml b/services/manifest_services.xml
deleted file mode 100644
index 9457205..0000000
--- a/services/manifest_services.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-<manifest version="1.0" type="framework">
-    <hal format="aidl">
-        <name>android.frameworks.location.altitude</name>
-        <version>2</version>
-        <fqname>IAltitudeService/default</fqname>
-    </hal>
-    <hal format="aidl">
-        <name>android.frameworks.devicestate</name>
-        <version>1</version>
-        <fqname>IDeviceStateService/default</fqname>
-    </hal>
-</manifest>
diff --git a/services/manifest_services_android.frameworks.devicestate.xml b/services/manifest_services_android.frameworks.devicestate.xml
new file mode 100644
index 0000000..dc189ec
--- /dev/null
+++ b/services/manifest_services_android.frameworks.devicestate.xml
@@ -0,0 +1,7 @@
+<manifest version="1.0" type="framework">
+    <hal format="aidl">
+        <name>android.frameworks.devicestate</name>
+        <version>1</version>
+        <fqname>IDeviceStateService/default</fqname>
+    </hal>
+</manifest>
diff --git a/services/manifest_services_android.frameworks.location.xml b/services/manifest_services_android.frameworks.location.xml
new file mode 100644
index 0000000..114fe32
--- /dev/null
+++ b/services/manifest_services_android.frameworks.location.xml
@@ -0,0 +1,7 @@
+<manifest version="1.0" type="framework">
+    <hal format="aidl">
+        <name>android.frameworks.location.altitude</name>
+        <version>2</version>
+        <fqname>IAltitudeService/default</fqname>
+    </hal>
+</manifest>
diff --git a/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
index 0b7438c..018cf20 100644
--- a/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
@@ -464,6 +464,48 @@
         return size
     }
 
+    override fun getPermissionRequestState(
+        packageName: String,
+        permissionName: String,
+        deviceId: String
+    ): Int {
+        val uid = Binder.getCallingUid()
+        val result = context.checkPermission(permissionName, Binder.getCallingPid(), uid)
+        if (result == PackageManager.PERMISSION_GRANTED) {
+            return Context.PERMISSION_REQUEST_STATE_GRANTED
+        }
+
+        val appId = UserHandle.getAppId(uid)
+        val userId = UserHandle.getUserId(uid)
+        val packageState =
+                packageManagerLocal.withFilteredSnapshot(uid, userId).use {
+                    it.getPackageState(packageName)
+                } ?: return Context.PERMISSION_REQUEST_STATE_UNREQUESTABLE
+        val androidPackage = packageState.androidPackage
+                ?: return Context.PERMISSION_REQUEST_STATE_UNREQUESTABLE
+        if (appId != packageState.appId) {
+            return Context.PERMISSION_REQUEST_STATE_UNREQUESTABLE
+        }
+        val permission = service.getState {
+            with(policy) { getPermissions()[permissionName] }
+        }
+        if (permission == null || !permission.isRuntime) {
+            return Context.PERMISSION_REQUEST_STATE_UNREQUESTABLE
+        }
+        if (permissionName !in androidPackage.requestedPermissions) {
+            return Context.PERMISSION_REQUEST_STATE_UNREQUESTABLE
+        }
+
+        val permissionFlags = service.getState {
+            getPermissionFlagsWithPolicy(appId, userId, permissionName, deviceId)
+        }
+        return if (permissionFlags.hasAnyBit(UNREQUESTABLE_MASK)) {
+            Context.PERMISSION_REQUEST_STATE_UNREQUESTABLE
+        } else {
+            Context.PERMISSION_REQUEST_STATE_REQUESTABLE
+        }
+    }
+
     override fun checkUidPermission(uid: Int, permissionName: String, deviceId: String): Int {
         val userId = UserHandle.getUserId(uid)
         if (!userManagerInternal.exists(userId)) {
@@ -472,7 +514,7 @@
 
         // PackageManagerInternal.getPackage(int) already checks package visibility and enforces
         // that instant apps can't see shared UIDs. Note that on the contrary,
-        // Note that PackageManagerInternal.getPackage(String) doesn't perform any checks.
+        // PackageManagerInternal.getPackage(String) doesn't perform any checks.
         val androidPackage = packageManagerInternal.getPackage(uid)
         if (androidPackage != null) {
             // Note that PackageManagerInternal.getPackageStateInternal() is not filtered.
diff --git a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
index 228e32e..c31594a 100644
--- a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
+++ b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
@@ -16,6 +16,9 @@
 
 package com.android.server.profcollect;
 
+import static android.content.Intent.ACTION_SCREEN_OFF;
+import static android.content.Intent.ACTION_SCREEN_ON;
+
 import android.Manifest;
 import android.annotation.RequiresPermission;
 import android.app.job.JobInfo;
@@ -32,6 +35,7 @@
 import android.os.Handler;
 import android.os.IBinder.DeathRecipient;
 import android.os.Looper;
+import android.os.PowerManager;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemProperties;
@@ -70,10 +74,11 @@
     private int mUsageSetting;
     private boolean mUploadEnabled;
 
-    private static boolean sVerityEnforced;
-    private boolean mAdbActive;
+    static boolean sVerityEnforced;
+    static boolean sIsInteractive;
+    static boolean sAdbActive;
 
-    private IProfCollectd mIProfcollect;
+    private static IProfCollectd sIProfcollect;
     private static ProfcollectForwardingService sSelfService;
     private final Handler mHandler = new ProfcollectdHandler(IoThread.getHandler().getLooper());
 
@@ -86,17 +91,24 @@
     private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
-            if (INTENT_UPLOAD_PROFILES.equals(intent.getAction())) {
+            if (ACTION_SCREEN_ON.equals(intent.getAction())) {
+                Log.d(LOG_TAG, "Received broadcast that the device became interactive, was "
+                        + sIsInteractive);
+                sIsInteractive = true;
+            } else if (ACTION_SCREEN_OFF.equals(intent.getAction())) {
+                Log.d(LOG_TAG, "Received broadcast that the device became noninteractive, was "
+                        + sIsInteractive);
+                sIsInteractive = false;
+            } else if (INTENT_UPLOAD_PROFILES.equals(intent.getAction())) {
                 Log.d(LOG_TAG, "Received broadcast to pack and upload reports");
                 createAndUploadReport(sSelfService);
-            }
-            if (UsbManager.ACTION_USB_STATE.equals(intent.getAction())) {
+            } else if (UsbManager.ACTION_USB_STATE.equals(intent.getAction())) {
                 boolean isADB = intent.getBooleanExtra(UsbManager.USB_FUNCTION_ADB, false);
                 if (isADB) {
                     boolean connected = intent.getBooleanExtra(UsbManager.USB_CONNECTED, false);
                     Log.d(LOG_TAG, "Received broadcast that ADB became " + connected
-                            + ", was " + mAdbActive);
-                    mAdbActive = connected;
+                            + ", was " + sAdbActive);
+                    sAdbActive = connected;
                 }
             }
         }
@@ -129,6 +141,8 @@
             context.getResources().getBoolean(R.bool.config_profcollectReportUploaderEnabled);
 
         final IntentFilter filter = new IntentFilter();
+        filter.addAction(ACTION_SCREEN_ON);
+        filter.addAction(ACTION_SCREEN_OFF);
         filter.addAction(INTENT_UPLOAD_PROFILES);
         filter.addAction(UsbManager.ACTION_USB_STATE);
         context.registerReceiver(mBroadcastReceiver, filter, Context.RECEIVER_NOT_EXPORTED);
@@ -153,14 +167,24 @@
         if (phase == PHASE_SYSTEM_SERVICES_READY) {
             UsbManager usbManager = getContext().getSystemService(UsbManager.class);
             if (usbManager == null) {
-                mAdbActive = false;
-                return;
+                sAdbActive = false;
+                Log.d(LOG_TAG, "USBManager is not ready");
+            } else {
+                sAdbActive = ((usbManager.getCurrentFunctions() & UsbManager.FUNCTION_ADB) == 1);
+                Log.d(LOG_TAG, "ADB is " + sAdbActive + " on system startup");
             }
-            mAdbActive = ((usbManager.getCurrentFunctions() & UsbManager.FUNCTION_ADB) == 1);
-            Log.d(LOG_TAG, "ADB is " + mAdbActive + " on system startup");
+
+            PowerManager powerManager = getContext().getSystemService(PowerManager.class);
+            if (powerManager == null) {
+                sIsInteractive = true;
+                Log.d(LOG_TAG, "PowerManager is not ready");
+            } else {
+                sIsInteractive = powerManager.isInteractive();
+                Log.d(LOG_TAG, "Device is interactive " + sIsInteractive + " on system startup");
+            }
         }
         if (phase == PHASE_BOOT_COMPLETED) {
-            if (mIProfcollect == null) {
+            if (sIProfcollect == null) {
                 return;
             }
             BackgroundThread.get().getThreadHandler().post(() -> {
@@ -172,22 +196,22 @@
     }
 
     private void registerProviderStatusCallback() {
-        if (mIProfcollect == null) {
+        if (sIProfcollect == null) {
             return;
         }
         try {
-            mIProfcollect.registerProviderStatusCallback(mProviderStatusCallback);
+            sIProfcollect.registerProviderStatusCallback(mProviderStatusCallback);
         } catch (RemoteException e) {
             Log.e(LOG_TAG, "Failed to register provider status callback: " + e.getMessage());
         }
     }
 
     private boolean serviceHasSupportedTraceProvider() {
-        if (mIProfcollect == null) {
+        if (sIProfcollect == null) {
             return false;
         }
         try {
-            return !mIProfcollect.get_supported_provider().isEmpty();
+            return !sIProfcollect.get_supported_provider().isEmpty();
         } catch (RemoteException e) {
             Log.e(LOG_TAG, "Failed to get supported provider: " + e.getMessage());
             return false;
@@ -209,7 +233,7 @@
                     IProfCollectd.Stub.asInterface(
                             ServiceManager.getServiceOrThrow("profcollectd"));
             profcollectd.asBinder().linkToDeath(new ProfcollectdDeathRecipient(), /*flags*/0);
-            mIProfcollect = profcollectd;
+            sIProfcollect = profcollectd;
             return true;
         } catch (ServiceManager.ServiceNotFoundException | RemoteException e) {
             Log.w(LOG_TAG, "Failed to connect profcollectd binder service.");
@@ -233,7 +257,8 @@
                     break;
                 case MESSAGE_REGISTER_SCHEDULERS:
                     registerObservers();
-                    ProfcollectBGJobService.schedule(getContext());
+                    PeriodicTraceJobService.schedule(getContext());
+                    ReportProcessJobService.schedule(getContext());
                     break;
                 default:
                     throw new AssertionError("Unknown message: " + message);
@@ -246,27 +271,66 @@
         public void binderDied() {
             Log.w(LOG_TAG, "profcollectd has died");
 
-            mIProfcollect = null;
+            sIProfcollect = null;
             tryConnectNativeService();
         }
     }
 
     /**
-     * Background trace process service.
+     * Background report process and upload service.
      */
-    public static class ProfcollectBGJobService extends JobService {
-        // Unique ID in system service
-        private static final int JOB_IDLE_PROCESS = 260817;
+    public static class PeriodicTraceJobService extends JobService {
+        // Unique ID in system server
+        private static final int PERIODIC_TRACE_JOB_ID = 241207;
         private static final ComponentName JOB_SERVICE_NAME = new ComponentName(
                 "android",
-                ProfcollectBGJobService.class.getName());
+                PeriodicTraceJobService.class.getName());
+
+        /**
+         * Attach the service to the system job scheduler.
+         */
+        public static void schedule(Context context) {
+            final int interval = DeviceConfig.getInt(DeviceConfig.NAMESPACE_PROFCOLLECT_NATIVE_BOOT,
+                    "collection_interval", 600);
+            JobScheduler js = context.getSystemService(JobScheduler.class);
+            js.schedule(new JobInfo.Builder(PERIODIC_TRACE_JOB_ID, JOB_SERVICE_NAME)
+                    .setPeriodic(TimeUnit.SECONDS.toMillis(interval))
+                    // PRIORITY_DEFAULT is the highest priority we can request for a periodic job.
+                    .setPriority(JobInfo.PRIORITY_DEFAULT)
+                    .build());
+        }
+
+        @Override
+        public boolean onStartJob(JobParameters params) {
+            if (sIProfcollect != null) {
+                Utils.traceSystem(sIProfcollect, "periodic");
+            }
+            jobFinished(params, false);
+            return true;
+        }
+
+        @Override
+        public boolean onStopJob(JobParameters params) {
+            return false;
+        }
+    }
+
+    /**
+     * Background report process and upload service.
+     */
+    public static class ReportProcessJobService extends JobService {
+        // Unique ID in system server
+        private static final int REPORT_PROCESS_JOB_ID = 260817;
+        private static final ComponentName JOB_SERVICE_NAME = new ComponentName(
+                "android",
+                ReportProcessJobService.class.getName());
 
         /**
          * Attach the service to the system job scheduler.
          */
         public static void schedule(Context context) {
             JobScheduler js = context.getSystemService(JobScheduler.class);
-            js.schedule(new JobInfo.Builder(JOB_IDLE_PROCESS, JOB_SERVICE_NAME)
+            js.schedule(new JobInfo.Builder(REPORT_PROCESS_JOB_ID, JOB_SERVICE_NAME)
                     .setRequiresDeviceIdle(true)
                     .setRequiresCharging(true)
                     .setPeriodic(BG_PROCESS_INTERVAL)
@@ -283,7 +347,6 @@
 
         @Override
         public boolean onStopJob(JobParameters params) {
-            // TODO: Handle this?
             return false;
         }
     }
@@ -311,14 +374,8 @@
     private class AppLaunchObserver extends ActivityMetricsLaunchObserver {
         @Override
         public void onIntentStarted(Intent intent, long timestampNanos) {
-            if (mIProfcollect == null) {
-                return;
-            }
-            if (mAdbActive) {
-                return;
-            }
             if (Utils.withFrequency("applaunch_trace_freq", 5)) {
-                Utils.traceSystem(mIProfcollect, "applaunch");
+                Utils.traceSystem(sIProfcollect, "applaunch");
             }
         }
     }
@@ -336,15 +393,9 @@
     }
 
     private void traceOnDex2oatStart() {
-        if (mIProfcollect == null) {
-            return;
-        }
-        if (mAdbActive) {
-            return;
-        }
         if (Utils.withFrequency("dex2oat_trace_freq", 25)) {
             // Dex2oat could take a while before it starts. Add a short delay before start tracing.
-            Utils.traceSystem(mIProfcollect, "dex2oat", /* delayMs */ 1000);
+            Utils.traceSystem(sIProfcollect, "dex2oat", /* delayMs */ 1000);
         }
     }
 
@@ -367,12 +418,12 @@
 
     private static void createAndUploadReport(ProfcollectForwardingService pfs) {
         BackgroundThread.get().getThreadHandler().post(() -> {
-            if (pfs.mIProfcollect == null) {
+            if (pfs.sIProfcollect == null) {
                 return;
             }
             String reportName;
             try {
-                reportName = pfs.mIProfcollect.report(pfs.mUsageSetting) + ".zip";
+                reportName = pfs.sIProfcollect.report(pfs.mUsageSetting) + ".zip";
             } catch (RemoteException e) {
                 Log.e(LOG_TAG, "Failed to create report: " + e.getMessage());
                 return;
@@ -411,7 +462,7 @@
                     return;
                 }
                 if (Utils.withFrequency("camera_trace_freq", 10)) {
-                    Utils.traceProcess(mIProfcollect,
+                    Utils.traceProcess(sIProfcollect,
                             "camera",
                             "android.hardware.camera.provider",
                             /* durationMs */ 5000);
diff --git a/services/profcollect/src/com/android/server/profcollect/Utils.java b/services/profcollect/src/com/android/server/profcollect/Utils.java
index a8016a0..b754ca1 100644
--- a/services/profcollect/src/com/android/server/profcollect/Utils.java
+++ b/services/profcollect/src/com/android/server/profcollect/Utils.java
@@ -28,28 +28,29 @@
 import java.time.Instant;
 import java.util.concurrent.ThreadLocalRandom;
 
-public final class Utils {
+final class Utils {
 
     private static Instant lastTraceTime = Instant.EPOCH;
     private static final int TRACE_COOLDOWN_SECONDS = 30;
 
-    public static boolean withFrequency(String configName, int defaultFrequency) {
+    static boolean withFrequency(String configName, int defaultFrequency) {
         int threshold = DeviceConfig.getInt(
                 DeviceConfig.NAMESPACE_PROFCOLLECT_NATIVE_BOOT, configName, defaultFrequency);
         int randomNum = ThreadLocalRandom.current().nextInt(100);
         return randomNum < threshold;
     }
 
-    public static boolean traceSystem(IProfCollectd mIProfcollect, String eventName) {
-        if (mIProfcollect == null) {
-            return false;
-        }
-        if (isInCooldownOrReset()) {
+    /**
+     * Request a system-wide trace.
+     * Will be ignored if the device does not meet trace criteria or is being rate limited.
+     */
+    static boolean traceSystem(IProfCollectd iprofcollectd, String eventName) {
+        if (!checkPrerequisites(iprofcollectd)) {
             return false;
         }
         BackgroundThread.get().getThreadHandler().post(() -> {
             try {
-                mIProfcollect.trace_system(eventName);
+                iprofcollectd.trace_system(eventName);
             } catch (RemoteException | ServiceSpecificException e) {
                 Log.e(LOG_TAG, "Failed to initiate trace: " + e.getMessage());
             }
@@ -57,16 +58,17 @@
         return true;
     }
 
-    public static boolean traceSystem(IProfCollectd mIProfcollect, String eventName, int delayMs) {
-        if (mIProfcollect == null) {
-            return false;
-        }
-        if (isInCooldownOrReset()) {
+    /**
+     * Request a system-wide trace after a delay.
+     * Will be ignored if the device does not meet trace criteria or is being rate limited.
+     */
+    static boolean traceSystem(IProfCollectd iprofcollectd, String eventName, int delayMs) {
+        if (!checkPrerequisites(iprofcollectd)) {
             return false;
         }
         BackgroundThread.get().getThreadHandler().postDelayed(() -> {
             try {
-                mIProfcollect.trace_system(eventName);
+                iprofcollectd.trace_system(eventName);
             } catch (RemoteException | ServiceSpecificException e) {
                 Log.e(LOG_TAG, "Failed to initiate trace: " + e.getMessage());
             }
@@ -74,17 +76,18 @@
         return true;
     }
 
-    public static boolean traceProcess(IProfCollectd mIProfcollect,
+    /**
+     * Request a single-process trace.
+     * Will be ignored if the device does not meet trace criteria or is being rate limited.
+     */
+    static boolean traceProcess(IProfCollectd iprofcollectd,
             String eventName, String processName, int durationMs) {
-        if (mIProfcollect == null) {
-            return false;
-        }
-        if (isInCooldownOrReset()) {
+        if (!checkPrerequisites(iprofcollectd)) {
             return false;
         }
         BackgroundThread.get().getThreadHandler().post(() -> {
             try {
-                mIProfcollect.trace_process(eventName,
+                iprofcollectd.trace_process(eventName,
                         processName,
                         durationMs);
             } catch (RemoteException | ServiceSpecificException e) {
@@ -105,4 +108,16 @@
         }
         return true;
     }
+
+    private static boolean checkPrerequisites(IProfCollectd iprofcollectd) {
+        if (iprofcollectd == null) {
+            return false;
+        }
+        if (isInCooldownOrReset()) {
+            return false;
+        }
+        return ProfcollectForwardingService.sVerityEnforced
+            && !ProfcollectForwardingService.sAdbActive
+            && ProfcollectForwardingService.sIsInteractive;
+    }
 }
diff --git a/services/supervision/java/com/android/server/supervision/SupervisionService.java b/services/supervision/java/com/android/server/supervision/SupervisionService.java
index 0ccaa60..073ee31 100644
--- a/services/supervision/java/com/android/server/supervision/SupervisionService.java
+++ b/services/supervision/java/com/android/server/supervision/SupervisionService.java
@@ -16,6 +16,11 @@
 
 package com.android.server.supervision;
 
+import static android.Manifest.permission.INTERACT_ACROSS_USERS;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+
+import static com.android.internal.util.Preconditions.checkCallAuthorization;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
@@ -31,6 +36,7 @@
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
 import android.content.pm.UserInfo;
+import android.os.Binder;
 import android.os.PersistableBundle;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
@@ -78,6 +84,9 @@
 
     @Override
     public boolean isSupervisionEnabledForUser(@UserIdInt int userId) {
+        if (UserHandle.getUserId(Binder.getCallingUid()) != userId) {
+            enforcePermission(INTERACT_ACROSS_USERS);
+        }
         synchronized (getLockObject()) {
             return getUserDataLocked(userId).supervisionEnabled;
         }
@@ -151,7 +160,8 @@
 
     /** Returns whether the supervision app has profile owner status. */
     private boolean isProfileOwner(@UserIdInt int userId) {
-        ComponentName profileOwner = mDpmInternal.getProfileOwnerAsUser(userId);
+        ComponentName profileOwner =
+                mDpmInternal != null ? mDpmInternal.getProfileOwnerAsUser(userId) : null;
         return profileOwner != null && isSupervisionAppPackage(profileOwner.getPackageName());
     }
 
@@ -161,6 +171,12 @@
                 mContext.getResources().getString(R.string.config_systemSupervision));
     }
 
+    /** Enforces that the caller has the given permission. */
+    private void enforcePermission(String permission) {
+        checkCallAuthorization(
+                mContext.checkCallingOrSelfPermission(permission) == PERMISSION_GRANTED);
+    }
+
     public static class Lifecycle extends SystemService {
         private final SupervisionService mSupervisionService;
 
diff --git a/services/tests/VpnTests/java/com/android/server/connectivity/VpnTest.java b/services/tests/VpnTests/java/com/android/server/connectivity/VpnTest.java
index 5db6a8f..9117cc8 100644
--- a/services/tests/VpnTests/java/com/android/server/connectivity/VpnTest.java
+++ b/services/tests/VpnTests/java/com/android/server/connectivity/VpnTest.java
@@ -918,6 +918,30 @@
     }
 
     @Test
+    public void testOnUserAddedAndRemoved_nullUserInfo() throws Exception {
+        final Vpn vpn = createVpn(PRIMARY_USER.id);
+        final Set<Range<Integer>> initialRange = rangeSet(PRIMARY_USER_RANGE);
+        // Note since mVpnProfile is a Ikev2VpnProfile, this starts an IkeV2VpnRunner.
+        startLegacyVpn(vpn, mVpnProfile);
+        // Set an initial Uid range and mock the network agent
+        vpn.mNetworkCapabilities.setUids(initialRange);
+        vpn.mNetworkAgent = mMockNetworkAgent;
+
+        // Add the restricted user and then remove it immediately. So the getUserInfo() will return
+        // null for the given restricted user id.
+        setMockedUsers(PRIMARY_USER, RESTRICTED_PROFILE_A);
+        doReturn(null).when(mUserManager).getUserInfo(RESTRICTED_PROFILE_A.id);
+        vpn.onUserAdded(RESTRICTED_PROFILE_A.id);
+        // Expect no range change to the NetworkCapabilities.
+        assertEquals(initialRange, vpn.mNetworkCapabilities.getUids());
+
+        // Remove the restricted user
+        vpn.onUserRemoved(RESTRICTED_PROFILE_A.id);
+        // Expect no range change to the NetworkCapabilities.
+        assertEquals(initialRange, vpn.mNetworkCapabilities.getUids());
+    }
+
+    @Test
     public void testPrepare_throwSecurityExceptionWhenGivenPackageDoesNotBelongToTheCaller()
             throws Exception {
         mTestDeps.mIgnoreCallingUidChecks = false;
diff --git a/services/tests/displayservicetests/src/com/android/server/display/BrightnessRangeControllerTest.kt b/services/tests/displayservicetests/src/com/android/server/display/BrightnessRangeControllerTest.kt
index f589a2c..7db6ea0 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/BrightnessRangeControllerTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/BrightnessRangeControllerTest.kt
@@ -60,12 +60,6 @@
     }
 
     @Test
-    fun testMaxBrightness_HbmDisabledAndNotAllowed() {
-        val controller = createController(nbmEnabled = false, hbmAllowed = false)
-        assertThat(controller.currentBrightnessMax).isEqualTo(MAX_BRIGHTNESS)
-    }
-
-    @Test
     fun testMaxBrightness_transitionPointLessThanCurrentNbmLimit() {
         val controller = createController(
             hbmAllowed = false,
@@ -76,13 +70,11 @@
     }
 
     private fun createController(
-        nbmEnabled: Boolean = true,
         hbmSupported: Boolean = true,
         hbmAllowed: Boolean = true,
         hbmMaxBrightness: Float = MAX_BRIGHTNESS,
         nbmMaxBrightness: Float = NORMAL_BRIGHTNESS_LOW
     ): BrightnessRangeController {
-        whenever(mockFlags.isNbmControllerEnabled).thenReturn(nbmEnabled)
         whenever(mockHbmController.deviceSupportsHbm()).thenReturn(hbmSupported)
         whenever(mockHbmController.isHbmCurrentlyAllowed).thenReturn(hbmAllowed)
         whenever(mockHbmController.currentBrightnessMax).thenReturn(hbmMaxBrightness)
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
index 724f083..f96294ed 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -30,6 +30,7 @@
 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION;
 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_TRUSTED;
 import static android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS;
+import static android.provider.Settings.Secure.MIRROR_BUILT_IN_DISPLAY;
 import static android.view.ContentRecordingSession.RECORD_CONTENT_DISPLAY;
 import static android.view.ContentRecordingSession.RECORD_CONTENT_TASK;
 import static android.view.Display.HdrCapabilities.HDR_TYPE_INVALID;
@@ -88,6 +89,7 @@
 import android.content.pm.PackageManagerInternal;
 import android.content.pm.UserInfo;
 import android.content.res.Resources;
+import android.database.ContentObserver;
 import android.graphics.Insets;
 import android.graphics.Rect;
 import android.hardware.Sensor;
@@ -3830,6 +3832,96 @@
         assertThat(callback.receivedEvents()).isEmpty();
     }
 
+    @Test
+    public void testMirrorBuiltInDisplay_flagEnabled() {
+        when(mMockFlags.isDisplayContentModeManagementEnabled()).thenReturn(true);
+        Settings.Secure.putInt(mContext.getContentResolver(), MIRROR_BUILT_IN_DISPLAY, 0);
+
+        DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
+        displayManager.systemReady(/* safeMode= */ false);
+        assertThat(displayManager.shouldMirrorBuiltInDisplay()).isFalse();
+
+        Settings.Secure.putInt(mContext.getContentResolver(), MIRROR_BUILT_IN_DISPLAY, 1);
+        final ContentObserver observer = displayManager.getSettingsObserver();
+        observer.onChange(false, Settings.Secure.getUriFor(MIRROR_BUILT_IN_DISPLAY));
+        assertThat(displayManager.shouldMirrorBuiltInDisplay()).isTrue();
+    }
+
+    @Test
+    public void testMirrorBuiltInDisplay_flagDisabled() {
+        when(mMockFlags.isDisplayContentModeManagementEnabled()).thenReturn(false);
+        Settings.Secure.putInt(mContext.getContentResolver(), MIRROR_BUILT_IN_DISPLAY, 0);
+
+        DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
+        displayManager.systemReady(/* safeMode= */ false);
+        assertThat(displayManager.shouldMirrorBuiltInDisplay()).isFalse();
+
+        Settings.Secure.putInt(mContext.getContentResolver(), MIRROR_BUILT_IN_DISPLAY, 1);
+        final ContentObserver observer = displayManager.getSettingsObserver();
+        observer.onChange(false, Settings.Secure.getUriFor(MIRROR_BUILT_IN_DISPLAY));
+        assertThat(displayManager.shouldMirrorBuiltInDisplay()).isFalse();
+    }
+
+    @Test
+    public void testShouldNotNotifyDefaultDisplayChanges_whenMirrorBuiltInDisplayChanges() {
+        when(mMockFlags.isDisplayContentModeManagementEnabled()).thenReturn(true);
+        Settings.Secure.putInt(mContext.getContentResolver(), MIRROR_BUILT_IN_DISPLAY, 0);
+
+        DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
+        displayManager.systemReady(/* safeMode= */ false);
+
+        DisplayManagerService.BinderService displayManagerBinderService =
+                displayManager.new BinderService();
+        Handler handler = displayManager.getDisplayHandler();
+        waitForIdleHandler(handler);
+
+        FakeDisplayManagerCallback callback = new FakeDisplayManagerCallback();
+        displayManagerBinderService.registerCallbackWithEventMask(
+                callback, STANDARD_DISPLAY_EVENTS);
+        waitForIdleHandler(handler);
+
+        // Create a default display device
+        createFakeDisplayDevice(displayManager, new float[] {60f}, Display.TYPE_INTERNAL);
+
+        Settings.Secure.putInt(mContext.getContentResolver(), MIRROR_BUILT_IN_DISPLAY, 1);
+        final ContentObserver observer = displayManager.getSettingsObserver();
+        observer.onChange(false, Settings.Secure.getUriFor(MIRROR_BUILT_IN_DISPLAY));
+        waitForIdleHandler(handler);
+
+        assertThat(callback.receivedEvents()).doesNotContain(EVENT_DISPLAY_CHANGED);
+    }
+
+    @Test
+    public void testShouldNotifyNonDefaultDisplayChanges_whenMirrorBuiltInDisplayChanges() {
+        when(mMockFlags.isDisplayContentModeManagementEnabled()).thenReturn(true);
+        Settings.Secure.putInt(mContext.getContentResolver(), MIRROR_BUILT_IN_DISPLAY, 0);
+
+        DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
+        displayManager.systemReady(/* safeMode= */ false);
+
+        DisplayManagerService.BinderService displayManagerBinderService =
+                displayManager.new BinderService();
+        Handler handler = displayManager.getDisplayHandler();
+        waitForIdleHandler(handler);
+
+        FakeDisplayManagerCallback callback = new FakeDisplayManagerCallback();
+        displayManagerBinderService.registerCallbackWithEventMask(
+                callback, STANDARD_DISPLAY_EVENTS);
+        waitForIdleHandler(handler);
+
+        // Create a default display device
+        createFakeDisplayDevice(displayManager, new float[] {60f}, Display.TYPE_INTERNAL);
+        // Create a non-default display device
+        createFakeDisplayDevice(displayManager, new float[] {60f}, Display.TYPE_EXTERNAL);
+
+        Settings.Secure.putInt(mContext.getContentResolver(), MIRROR_BUILT_IN_DISPLAY, 1);
+        final ContentObserver observer = displayManager.getSettingsObserver();
+        observer.onChange(false, Settings.Secure.getUriFor(MIRROR_BUILT_IN_DISPLAY));
+        waitForIdleHandler(handler);
+
+        assertThat(callback.receivedEvents()).contains(EVENT_DISPLAY_CHANGED);
+    }
+
     private void initDisplayPowerController(DisplayManagerInternal localService) {
         localService.initPowerManagement(new DisplayManagerInternal.DisplayPowerCallbacks() {
             @Override
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
index 8ca3919..a4dfecb 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
@@ -2626,8 +2626,8 @@
                 mock(ScreenOffBrightnessSensorController.class);
         final HighBrightnessModeController hbmController = mock(HighBrightnessModeController.class);
         final HdrClamper hdrClamper = mock(HdrClamper.class);
-        final NormalBrightnessModeController normalBrightnessModeController = mock(
-                NormalBrightnessModeController.class);
+        final NormalBrightnessModeController normalBrightnessModeController =
+                new NormalBrightnessModeController();
         BrightnessClamperController clamperController = mock(BrightnessClamperController.class);
 
         when(hbmController.getCurrentBrightnessMax()).thenReturn(PowerManager.BRIGHTNESS_MAX);
diff --git a/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayTest.java b/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayTest.java
index 241dc10..1a0ab25 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayTest.java
@@ -609,4 +609,69 @@
         DisplayInfo info = mLogicalDisplay.getDisplayInfoLocked();
         assertArrayEquals(appSupportedModes, info.appsSupportedModes);
     }
+
+    @Test
+    public void testSetCanHostTasks_defaultDisplay() {
+        mLogicalDisplay = new LogicalDisplay(Display.DEFAULT_DISPLAY, LAYER_STACK, mDisplayDevice);
+        assertTrue(mLogicalDisplay.canHostTasksLocked());
+
+        mLogicalDisplay.setCanHostTasksLocked(true);
+        assertTrue(mLogicalDisplay.canHostTasksLocked());
+
+        mLogicalDisplay.setCanHostTasksLocked(false);
+        assertTrue(mLogicalDisplay.canHostTasksLocked());
+    }
+
+    @Test
+    public void testSetCanHostTasks_nonDefaultNormalDisplay() {
+        mLogicalDisplay =
+                new LogicalDisplay(Display.DEFAULT_DISPLAY + 1, LAYER_STACK, mDisplayDevice);
+
+        mLogicalDisplay.setCanHostTasksLocked(true);
+        assertTrue(mLogicalDisplay.canHostTasksLocked());
+
+        mLogicalDisplay.setCanHostTasksLocked(false);
+        assertFalse(mLogicalDisplay.canHostTasksLocked());
+    }
+
+    @Test
+    public void testSetCanHostTasks_nonDefaultVirtualMirrorDisplay() {
+        mDisplayDeviceInfo.type = Display.TYPE_VIRTUAL;
+        when(mDisplayDevice.shouldOnlyMirror()).thenReturn(true);
+        mLogicalDisplay =
+                new LogicalDisplay(Display.DEFAULT_DISPLAY + 1, LAYER_STACK, mDisplayDevice);
+        mLogicalDisplay.updateLocked(mDeviceRepo, mSyntheticModeManager);
+
+        mLogicalDisplay.setCanHostTasksLocked(true);
+        assertFalse(mLogicalDisplay.canHostTasksLocked());
+
+        mLogicalDisplay.setCanHostTasksLocked(false);
+        assertFalse(mLogicalDisplay.canHostTasksLocked());
+    }
+
+    @Test
+    public void testSetCanHostTasks_nonDefaultRearDisplay() {
+        mLogicalDisplay =
+                new LogicalDisplay(Display.DEFAULT_DISPLAY + 1, LAYER_STACK, mDisplayDevice);
+        mLogicalDisplay.setDevicePositionLocked(Layout.Display.POSITION_REAR);
+
+        mLogicalDisplay.setCanHostTasksLocked(true);
+        assertTrue(mLogicalDisplay.canHostTasksLocked());
+
+        mLogicalDisplay.setCanHostTasksLocked(false);
+        assertTrue(mLogicalDisplay.canHostTasksLocked());
+    }
+
+    @Test
+    public void testSetCanHostTasks_nonDefaultOwnContentOnly() {
+        mDisplayDeviceInfo.flags = DisplayDeviceInfo.FLAG_OWN_CONTENT_ONLY;
+        mLogicalDisplay =
+                new LogicalDisplay(Display.DEFAULT_DISPLAY + 1, LAYER_STACK, mDisplayDevice);
+
+        mLogicalDisplay.setCanHostTasksLocked(true);
+        assertTrue(mLogicalDisplay.canHostTasksLocked());
+
+        mLogicalDisplay.setCanHostTasksLocked(false);
+        assertTrue(mLogicalDisplay.canHostTasksLocked());
+    }
 }
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/BrightnessEventTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/BrightnessEventTest.java
index df09b04..6d1e56d 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/BrightnessEventTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/BrightnessEventTest.java
@@ -68,6 +68,8 @@
         mBrightnessEvent.setAutomaticBrightnessEnabled(true);
         mBrightnessEvent.setDisplayBrightnessStrategyName(DISPLAY_BRIGHTNESS_STRATEGY_NAME);
         mBrightnessEvent.setAutoBrightnessMode(AUTO_BRIGHTNESS_MODE_IDLE);
+        mBrightnessEvent.setSlowChange(true);
+        mBrightnessEvent.setRampSpeed(0.3f);
     }
 
     @Test
@@ -88,7 +90,7 @@
                         + "preLux=150.0, wasShortTermModelActive=true, autoBrightness=true (idle), "
                         + "unclampedBrt=0.65, hbmMax=0.62, hbmMode=off, thrmMax=0.65, "
                         + "rbcStrength=-1, powerFactor=0.2, physDisp=display_name(987654321), "
-                        + "logicalId=1";
+                        + "logicalId=1, slowChange=true, rampSpeed=0.3";
         assertEquals(expectedString, actualString);
     }
 
diff --git a/services/tests/displayservicetests/src/com/android/server/display/color/ColorDisplayServiceTest.java b/services/tests/displayservicetests/src/com/android/server/display/color/ColorDisplayServiceTest.java
index f391e40..4e81b35 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/color/ColorDisplayServiceTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/color/ColorDisplayServiceTest.java
@@ -20,10 +20,13 @@
 import static com.google.common.truth.Truth.assertWithMessage;
 
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -89,6 +92,7 @@
     private ColorDisplayService.BinderService mBinderService;
 
     private Resources mResourcesSpy;
+    private ReduceBrightColorsTintController mRbcSpy;
 
     private static final int[] MINIMAL_COLOR_MODES = new int[] {
         ColorDisplayManager.COLOR_MODE_NATURAL,
@@ -135,7 +139,8 @@
         mLocalServiceKeeperRule.overrideLocalService(
                 DisplayManagerInternal.class, mDisplayManagerInternal);
 
-        mCds = new ColorDisplayService(mContext);
+        mRbcSpy = Mockito.spy(new ReduceBrightColorsTintController());
+        mCds = new ColorDisplayService(mContext, mRbcSpy);
         mBinderService = mCds.new BinderService();
         mLocalServiceKeeperRule.overrideLocalService(
                 ColorDisplayService.ColorDisplayServiceInternal.class,
@@ -1106,7 +1111,8 @@
         setColorMode(ColorDisplayManager.COLOR_MODE_NATURAL);
         startService();
         verify(mDisplayTransformManager).setColorMode(
-                eq(ColorDisplayManager.COLOR_MODE_NATURAL), any(), eq(Display.COLOR_MODE_INVALID));
+                eq(ColorDisplayManager.COLOR_MODE_NATURAL), any(), any(),
+                eq(Display.COLOR_MODE_INVALID));
     }
 
     @Test
@@ -1124,7 +1130,8 @@
         setColorMode(ColorDisplayManager.COLOR_MODE_NATURAL);
         startService();
         verify(mDisplayTransformManager).setColorMode(
-                eq(ColorDisplayManager.COLOR_MODE_NATURAL), any(), eq(Display.COLOR_MODE_INVALID));
+                eq(ColorDisplayManager.COLOR_MODE_NATURAL), any(), any(),
+                eq(Display.COLOR_MODE_INVALID));
     }
 
     @Test
@@ -1140,7 +1147,8 @@
         setColorMode(ColorDisplayManager.COLOR_MODE_NATURAL);
         startService();
         verify(mDisplayTransformManager).setColorMode(
-                eq(ColorDisplayManager.COLOR_MODE_NATURAL), any(), eq(Display.COLOR_MODE_SRGB));
+                eq(ColorDisplayManager.COLOR_MODE_NATURAL), any(), any(),
+                eq(Display.COLOR_MODE_SRGB));
     }
 
     @Test
@@ -1156,7 +1164,8 @@
         setColorMode(ColorDisplayManager.COLOR_MODE_BOOSTED);
         startService();
         verify(mDisplayTransformManager).setColorMode(
-                eq(ColorDisplayManager.COLOR_MODE_BOOSTED), any(), eq(Display.COLOR_MODE_INVALID));
+                eq(ColorDisplayManager.COLOR_MODE_BOOSTED), any(), any(),
+                eq(Display.COLOR_MODE_INVALID));
     }
 
     @Test
@@ -1164,10 +1173,27 @@
         when(mResourcesSpy.getIntArray(R.array.config_availableColorModes))
                     .thenReturn(new int[] {});
         startService();
-        verify(mDisplayTransformManager, never()).setColorMode(anyInt(), any(), anyInt());
+        verify(mDisplayTransformManager, never()).setColorMode(anyInt(), any(), any(), anyInt());
         assertThat(mBinderService.getColorMode()).isEqualTo(-1);
     }
 
+    @Test
+    public void ensureColorModeChangeTriggersRbcReload() {
+        // should set up RBC once at startup
+        startService();
+        reset(mRbcSpy);
+
+        // Make sure RBC is enabled and available for this test
+        doReturn(true).when(mRbcSpy).isAvailable(mContext);
+
+        // When Color Mode changes, RBC needs to re-setup
+        // onDisplayColorModeChanged cancels animations, and should be called in handler thread
+        mCds.mHandler.runWithScissors(
+                () -> mCds.onDisplayColorModeChanged(ColorDisplayManager.COLOR_MODE_NATURAL),
+                1000);
+        verify(mRbcSpy, times(1)).setUp(eq(mContext), anyBoolean());
+    }
+
     /**
      * Configures Night display to use a custom schedule.
      *
diff --git a/services/tests/displayservicetests/src/com/android/server/display/color/DisplayTransformManagerTest.java b/services/tests/displayservicetests/src/com/android/server/display/color/DisplayTransformManagerTest.java
index 27f87aa..a7ef5e0 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/color/DisplayTransformManagerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/color/DisplayTransformManagerTest.java
@@ -19,6 +19,7 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyString;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
 import static com.android.server.display.color.DisplayTransformManager.LEVEL_COLOR_MATRIX_NIGHT_DISPLAY;
+import static com.android.server.display.color.DisplayTransformManager.LEVEL_COLOR_MATRIX_REDUCE_BRIGHT_COLORS;
 import static com.android.server.display.color.DisplayTransformManager.PERSISTENT_PROPERTY_COMPOSITION_COLOR_MODE;
 import static com.android.server.display.color.DisplayTransformManager.PERSISTENT_PROPERTY_DISPLAY_COLOR;
 import static com.android.server.display.color.DisplayTransformManager.PERSISTENT_PROPERTY_SATURATION;
@@ -51,12 +52,14 @@
     private MockitoSession mSession;
     private DisplayTransformManager mDtm;
     private float[] mNightDisplayMatrix;
+    private float[] mRbcMatrix;
     private HashMap<String, String> mSystemProperties;
 
     @Before
     public void setUp() {
         mDtm = new DisplayTransformManager();
         mNightDisplayMatrix = mDtm.getColorMatrix(LEVEL_COLOR_MATRIX_NIGHT_DISPLAY);
+        mRbcMatrix = mDtm.getColorMatrix(LEVEL_COLOR_MATRIX_REDUCE_BRIGHT_COLORS);
 
         mSession = ExtendedMockito.mockitoSession()
                 .initMocks(this)
@@ -81,7 +84,8 @@
 
     @Test
     public void setColorMode_natural() {
-        mDtm.setColorMode(ColorDisplayManager.COLOR_MODE_NATURAL, mNightDisplayMatrix, -1);
+        mDtm.setColorMode(ColorDisplayManager.COLOR_MODE_NATURAL, mNightDisplayMatrix, mRbcMatrix,
+                Display.COLOR_MODE_INVALID);
         assertThat(mSystemProperties.get(PERSISTENT_PROPERTY_DISPLAY_COLOR))
                 .isEqualTo("0" /* managed */);
         assertThat(mSystemProperties.get(PERSISTENT_PROPERTY_SATURATION))
@@ -90,7 +94,8 @@
 
     @Test
     public void setColorMode_boosted() {
-        mDtm.setColorMode(ColorDisplayManager.COLOR_MODE_BOOSTED, mNightDisplayMatrix, -1);
+        mDtm.setColorMode(ColorDisplayManager.COLOR_MODE_BOOSTED, mNightDisplayMatrix, mRbcMatrix,
+                Display.COLOR_MODE_INVALID);
 
         assertThat(mSystemProperties.get(PERSISTENT_PROPERTY_DISPLAY_COLOR))
                 .isEqualTo("0" /* managed */);
@@ -100,7 +105,8 @@
 
     @Test
     public void setColorMode_saturated() {
-        mDtm.setColorMode(ColorDisplayManager.COLOR_MODE_SATURATED, mNightDisplayMatrix, -1);
+        mDtm.setColorMode(ColorDisplayManager.COLOR_MODE_SATURATED, mNightDisplayMatrix, mRbcMatrix,
+                Display.COLOR_MODE_INVALID);
         assertThat(mSystemProperties.get(PERSISTENT_PROPERTY_DISPLAY_COLOR))
                 .isEqualTo("1" /* unmanaged */);
         assertThat(mSystemProperties.get(PERSISTENT_PROPERTY_SATURATION))
@@ -109,7 +115,8 @@
 
     @Test
     public void setColorMode_automatic() {
-        mDtm.setColorMode(ColorDisplayManager.COLOR_MODE_AUTOMATIC, mNightDisplayMatrix, -1);
+        mDtm.setColorMode(ColorDisplayManager.COLOR_MODE_AUTOMATIC, mNightDisplayMatrix, mRbcMatrix,
+                Display.COLOR_MODE_INVALID);
         assertThat(mSystemProperties.get(PERSISTENT_PROPERTY_DISPLAY_COLOR))
                 .isEqualTo("2" /* enhanced */);
         assertThat(mSystemProperties.get(PERSISTENT_PROPERTY_SATURATION))
@@ -118,7 +125,7 @@
 
     @Test
     public void setColorMode_vendor() {
-        mDtm.setColorMode(0x100, mNightDisplayMatrix, -1);
+        mDtm.setColorMode(0x100, mNightDisplayMatrix, mRbcMatrix, Display.COLOR_MODE_INVALID);
         assertThat(mSystemProperties.get(PERSISTENT_PROPERTY_DISPLAY_COLOR))
                 .isEqualTo(Integer.toString(0x100) /* pass-through */);
         assertThat(mSystemProperties.get(PERSISTENT_PROPERTY_SATURATION))
@@ -127,7 +134,7 @@
 
     @Test
     public void setColorMode_outOfBounds() {
-        mDtm.setColorMode(0x50, mNightDisplayMatrix, -1);
+        mDtm.setColorMode(0x50, mNightDisplayMatrix, mRbcMatrix, Display.COLOR_MODE_INVALID);
         assertThat(mSystemProperties.get(PERSISTENT_PROPERTY_DISPLAY_COLOR))
                 .isEqualTo(null);
         assertThat(mSystemProperties.get(PERSISTENT_PROPERTY_SATURATION))
@@ -141,7 +148,7 @@
         // Start with a known state, which we expect to remain unmodified
         SystemProperties.set(PERSISTENT_PROPERTY_COMPOSITION_COLOR_MODE, magicPropertyValue);
 
-        mDtm.setColorMode(ColorDisplayManager.COLOR_MODE_NATURAL, mNightDisplayMatrix,
+        mDtm.setColorMode(ColorDisplayManager.COLOR_MODE_NATURAL, mNightDisplayMatrix, mRbcMatrix,
                 Display.COLOR_MODE_INVALID);
         assertThat(mSystemProperties.get(PERSISTENT_PROPERTY_COMPOSITION_COLOR_MODE))
                 .isEqualTo(magicPropertyValue);
@@ -155,7 +162,7 @@
         // Start with a known state, which we expect to get modified
         SystemProperties.set(PERSISTENT_PROPERTY_COMPOSITION_COLOR_MODE, magicPropertyValue);
 
-        mDtm.setColorMode(ColorDisplayManager.COLOR_MODE_NATURAL, mNightDisplayMatrix,
+        mDtm.setColorMode(ColorDisplayManager.COLOR_MODE_NATURAL, mNightDisplayMatrix, mRbcMatrix,
                 testPropertyValue);
         assertThat(mSystemProperties.get(PERSISTENT_PROPERTY_COMPOSITION_COLOR_MODE))
                 .isEqualTo(Integer.toString(testPropertyValue));
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 fc4cc25..f0a77bb 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
@@ -52,7 +52,9 @@
     @Before
     public void before() {
         MockitoAnnotations.initMocks(this);
-        mDisplayStateController = new DisplayStateController(mDisplayPowerProximityStateController);
+        final boolean shouldSkipScreenOffTransition = false;
+        mDisplayStateController = new DisplayStateController(
+                mDisplayPowerProximityStateController, /* shouldSkipScreenOffTransition= */ false);
     }
 
     @Test
@@ -236,6 +238,38 @@
         assertTrue(Display.STATE_REASON_OFFLOAD == stateAndReason.second);
     }
 
+    @Test
+    public void shouldPerformScreenOffTransition_whenRequestedOffAndNotConfiguredToSkip_true() {
+        mDisplayStateController = new DisplayStateController(
+                mDisplayPowerProximityStateController, /* shouldSkipScreenOffTransition= */ false);
+        when(mDisplayPowerProximityStateController.isScreenOffBecauseOfProximity()).thenReturn(
+                false);
+        DisplayManagerInternal.DisplayPowerRequest displayPowerRequest = mock(
+                DisplayManagerInternal.DisplayPowerRequest.class);
+
+        displayPowerRequest.policy = DisplayManagerInternal.DisplayPowerRequest.POLICY_OFF;
+        displayPowerRequest.policyReason = Display.STATE_REASON_KEY;
+        mDisplayStateController.updateDisplayState(
+                displayPowerRequest, DISPLAY_ENABLED, !DISPLAY_IN_TRANSITION);
+        assertEquals(true, mDisplayStateController.shouldPerformScreenOffTransition());
+    }
+
+    @Test
+    public void shouldPerformScreenOffTransition_whenRequestedOffAndConfiguredToSkip_false() {
+        mDisplayStateController = new DisplayStateController(
+                mDisplayPowerProximityStateController, /* shouldSkipScreenOffTransition= */ true);
+        when(mDisplayPowerProximityStateController.isScreenOffBecauseOfProximity()).thenReturn(
+                false);
+        DisplayManagerInternal.DisplayPowerRequest displayPowerRequest = mock(
+                DisplayManagerInternal.DisplayPowerRequest.class);
+
+        displayPowerRequest.policy = DisplayManagerInternal.DisplayPowerRequest.POLICY_OFF;
+        displayPowerRequest.policyReason = Display.STATE_REASON_KEY;
+        mDisplayStateController.updateDisplayState(
+                displayPowerRequest, DISPLAY_ENABLED, !DISPLAY_IN_TRANSITION);
+        assertEquals(false, mDisplayStateController.shouldPerformScreenOffTransition());
+    }
+
     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/RescuePartyTest.java b/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java
index f40d803..db04d39e 100644
--- a/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java
@@ -25,6 +25,8 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+import static com.android.server.PackageWatchdog.MITIGATION_RESULT_SKIPPED;
+import static com.android.server.PackageWatchdog.MITIGATION_RESULT_SUCCESS;
 import static com.android.server.RescueParty.DEFAULT_FACTORY_RESET_THROTTLE_DURATION_MIN;
 import static com.android.server.RescueParty.LEVEL_FACTORY_RESET;
 
@@ -357,11 +359,13 @@
         SystemProperties.set(RescueParty.PROP_ENABLE_RESCUE, Boolean.toString(false));
         SystemProperties.set(PROP_DISABLE_RESCUE, Boolean.toString(true));
         assertEquals(RescuePartyObserver.getInstance(mMockContext).onExecuteHealthCheckMitigation(
-                sFailingPackage, PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 1), false);
+                sFailingPackage, PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 1),
+                MITIGATION_RESULT_SKIPPED);
 
         SystemProperties.set(RescueParty.PROP_ENABLE_RESCUE, Boolean.toString(true));
-        assertTrue(RescuePartyObserver.getInstance(mMockContext).onExecuteHealthCheckMitigation(
-                sFailingPackage, PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 1));
+        assertEquals(RescuePartyObserver.getInstance(mMockContext).onExecuteHealthCheckMitigation(
+                sFailingPackage, PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 1),
+                MITIGATION_RESULT_SUCCESS);
     }
 
     @Test
@@ -370,7 +374,8 @@
         SystemProperties.set(PROP_DEVICE_CONFIG_DISABLE_FLAG, Boolean.toString(true));
 
         assertEquals(RescuePartyObserver.getInstance(mMockContext).onExecuteHealthCheckMitigation(
-                sFailingPackage, PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 1), false);
+                sFailingPackage, PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 1),
+                MITIGATION_RESULT_SKIPPED);
 
         // Restore the property value initialized in SetUp()
         SystemProperties.set(RescueParty.PROP_ENABLE_RESCUE, Boolean.toString(true));
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
index 1efe470..9e96800 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
@@ -18,6 +18,7 @@
 
 import static android.app.ActivityManager.PROCESS_CAPABILITY_ALL;
 import static android.app.ActivityManager.PROCESS_CAPABILITY_BFSL;
+import static android.app.ActivityManager.PROCESS_CAPABILITY_CPU_TIME;
 import static android.app.ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
 import static android.app.ActivityManager.PROCESS_STATE_BOUND_TOP;
 import static android.app.ActivityManager.PROCESS_STATE_CACHED_ACTIVITY;
@@ -40,6 +41,7 @@
 import static android.app.ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND;
 import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_ACTIVITY;
 import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_NONE;
+import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_SHORT_FGS_TIMEOUT;
 import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_SHORT_SERVICE;
 
 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
@@ -283,6 +285,15 @@
         }
     }
 
+    private static void assertNoCpuTime(ProcessRecord app) {
+        assertEquals(0, app.mState.getSetCapability() & PROCESS_CAPABILITY_CPU_TIME);
+    }
+
+    private static void assertCpuTime(ProcessRecord app) {
+        assertEquals(PROCESS_CAPABILITY_CPU_TIME,
+                app.mState.getSetCapability() & PROCESS_CAPABILITY_CPU_TIME);
+    }
+
     private static void assertBfsl(ProcessRecord app) {
         assertEquals(PROCESS_CAPABILITY_BFSL,
                 app.mState.getSetCapability() & PROCESS_CAPABILITY_BFSL);
@@ -661,6 +672,7 @@
         // SHORT_SERVICE, timed out already.
         s = ServiceRecord.newEmptyInstanceForTest(mService);
         s.appInfo = new ApplicationInfo();
+
         mProcessStateController.setStartRequested(s, true);
         s.isForeground = true;
         s.foregroundServiceType = FOREGROUND_SERVICE_TYPE_SHORT_SERVICE;
@@ -687,6 +699,51 @@
 
     @SuppressWarnings("GuardedBy")
     @Test
+    @EnableFlags(Flags.FLAG_USE_CPU_TIME_CAPABILITY)
+    public void testUpdateOomAdjFreezeState_bindingFromShortFgs() {
+        // Setting up a started short FGS within app1.
+        final ServiceRecord s = ServiceRecord.newEmptyInstanceForTest(mService);
+        s.appInfo = new ApplicationInfo();
+        mProcessStateController.setStartRequested(s, true);
+        s.isForeground = true;
+        s.foregroundServiceType = FOREGROUND_SERVICE_TYPE_SHORT_SERVICE;
+        mProcessStateController.setShortFgsInfo(s, SystemClock.uptimeMillis());
+
+        final ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
+                MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
+        mProcessStateController.setHostProcess(s, app);
+        mProcessStateController.setHasForegroundServices(app.mServices, true,
+                FOREGROUND_SERVICE_TYPE_SHORT_SERVICE, /* hasNoneType=*/false);
+        mProcessStateController.startService(app.mServices, s);
+        app.mState.setLastTopTime(SystemClock.uptimeMillis()
+                - mService.mConstants.TOP_TO_FGS_GRACE_DURATION);
+
+        final ProcessRecord app2 = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
+                MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
+        // App1 with short service binds to app2
+        bindService(app2, app, null, null, 0, mock(IBinder.class));
+
+        setProcessesToLru(app, app2);
+        updateOomAdj(app);
+
+        assertCpuTime(app);
+        assertCpuTime(app2);
+
+        // Timeout the short FGS.
+        mProcessStateController.setShortFgsInfo(s, SystemClock.uptimeMillis()
+                - mService.mConstants.mShortFgsTimeoutDuration
+                - mService.mConstants.mShortFgsProcStateExtraWaitDuration);
+        mService.mServices.onShortFgsProcstateTimeout(s);
+        // mService is a mock, but this verifies that the timeout would trigger an update.
+        verify(mService).updateOomAdjLocked(app, OOM_ADJ_REASON_SHORT_FGS_TIMEOUT);
+        updateOomAdj(app);
+
+        assertNoCpuTime(app);
+        assertNoCpuTime(app2);
+    }
+
+    @SuppressWarnings("GuardedBy")
+    @Test
     public void testUpdateOomAdj_DoOne_OverlayUi() {
         ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
@@ -3142,11 +3199,19 @@
         assertEquals(true, app.getUidRecord().isSetAllowListed());
         assertFreezeState(app, false);
         assertFreezeState(app2, false);
+        if (Flags.useCpuTimeCapability()) {
+            assertCpuTime(app);
+            assertCpuTime(app2);
+        }
 
         mProcessStateController.setUidTempAllowlistStateLSP(MOCKAPP_UID, false);
         assertEquals(false, app.getUidRecord().isSetAllowListed());
         assertFreezeState(app, true);
         assertFreezeState(app2, true);
+        if (Flags.useCpuTimeCapability()) {
+            assertNoCpuTime(app);
+            assertNoCpuTime(app2);
+        }
     }
 
     @SuppressWarnings("GuardedBy")
@@ -3171,6 +3236,11 @@
         assertFreezeState(app, false);
         assertFreezeState(app2, false);
         assertFreezeState(app3, false);
+        if (Flags.useCpuTimeCapability()) {
+            assertCpuTime(app);
+            assertCpuTime(app2);
+            assertCpuTime(app3);
+        }
 
         // Remove app1 from allowlist.
         mProcessStateController.setUidTempAllowlistStateLSP(MOCKAPP_UID, false);
@@ -3179,6 +3249,11 @@
         assertFreezeState(app, true);
         assertFreezeState(app2, false);
         assertFreezeState(app3, false);
+        if (Flags.useCpuTimeCapability()) {
+            assertNoCpuTime(app);
+            assertCpuTime(app2);
+            assertCpuTime(app3);
+        }
 
         // Now remove app2 from allowlist.
         mProcessStateController.setUidTempAllowlistStateLSP(MOCKAPP2_UID, false);
@@ -3187,6 +3262,11 @@
         assertFreezeState(app, true);
         assertFreezeState(app2, true);
         assertFreezeState(app3, true);
+        if (Flags.useCpuTimeCapability()) {
+            assertNoCpuTime(app);
+            assertNoCpuTime(app2);
+            assertNoCpuTime(app3);
+        }
     }
 
     @SuppressWarnings("GuardedBy")
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 2988c77..7e052dc 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/ServiceBindingOomAdjPolicyTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/ServiceBindingOomAdjPolicyTest.java
@@ -16,6 +16,7 @@
 
 package com.android.server.am;
 
+import static android.app.ActivityManager.PROCESS_CAPABILITY_CPU_TIME;
 import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_MICROPHONE;
 import static android.app.ActivityManager.PROCESS_CAPABILITY_NONE;
 import static android.app.ActivityManager.PROCESS_STATE_CACHED_EMPTY;
@@ -64,6 +65,8 @@
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.IBinder;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
 import android.platform.test.annotations.Presubmit;
 import android.platform.test.annotations.RequiresFlagsDisabled;
 import android.platform.test.annotations.RequiresFlagsEnabled;
@@ -326,6 +329,7 @@
 
     @Test
     @RequiresFlagsEnabled(com.android.server.am.Flags.FLAG_UNFREEZE_BIND_POLICY_FIX)
+    @DisableFlags(Flags.FLAG_USE_CPU_TIME_CAPABILITY)
     public void testServiceDistinctBindingOomAdjShouldNotFreeze() throws Exception {
         // Enable the flags.
         mSetFlagsRule.enableFlags(Flags.FLAG_SERVICE_BINDING_OOM_ADJ_POLICY);
@@ -418,6 +422,7 @@
 
     @Test
     @RequiresFlagsEnabled(com.android.server.am.Flags.FLAG_UNFREEZE_BIND_POLICY_FIX)
+    @DisableFlags(Flags.FLAG_USE_CPU_TIME_CAPABILITY)
     public void testServiceDistinctBindingOomAdjAllowOomManagement() throws Exception {
         // Enable the flags.
         mSetFlagsRule.enableFlags(Flags.FLAG_SERVICE_BINDING_OOM_ADJ_POLICY);
@@ -497,6 +502,7 @@
 
     @Test
     @RequiresFlagsEnabled(com.android.server.am.Flags.FLAG_UNFREEZE_BIND_POLICY_FIX)
+    @DisableFlags(Flags.FLAG_USE_CPU_TIME_CAPABILITY)
     public void testServiceDistinctBindingOomAdjWaivePriority_propagateUnfreeze() throws Exception {
         // Enable the flags.
         mSetFlagsRule.enableFlags(Flags.FLAG_SERVICE_BINDING_OOM_ADJ_POLICY);
@@ -574,6 +580,50 @@
     }
 
     @Test
+    @RequiresFlagsEnabled({
+            Flags.FLAG_UNFREEZE_BIND_POLICY_FIX,
+            Flags.FLAG_SERVICE_BINDING_OOM_ADJ_POLICY
+    })
+    @EnableFlags(Flags.FLAG_USE_CPU_TIME_CAPABILITY)
+    public void testServiceDistinctBindingOomAdj_propagateCpuTimeCapability() throws Exception {
+        // Note that PROCESS_CAPABILITY_CPU_TIME is special and should be propagated even when
+        // BIND_INCLUDE_CAPABILITIES is not present.
+        performTestServiceDistinctBindingOomAdj(TEST_APP1_PID, TEST_APP1_UID,
+                PROCESS_STATE_HOME, HOME_APP_ADJ, PROCESS_CAPABILITY_CPU_TIME, 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,
+                atLeastOnce(), atLeastOnce());
+
+        // BIND_WAIVE_PRIORITY should not affect propagation of capability CPU_TIME
+        performTestServiceDistinctBindingOomAdj(TEST_APP1_PID, TEST_APP1_UID,
+                PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, PROCESS_CAPABILITY_CPU_TIME,
+                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());
+
+        // If both process have the capability, the bind should not need an update but the unbind
+        // is not safe to skip.
+        // Note that this check can fail on future changes that are not related to
+        // PROCESS_CAPABILITY_CPU_TIME and trigger updates but this is important to ensure
+        // efficiency of OomAdjuster.
+        performTestServiceDistinctBindingOomAdj(TEST_APP1_PID, TEST_APP1_UID,
+                PROCESS_STATE_HOME, HOME_APP_ADJ, PROCESS_CAPABILITY_CPU_TIME, TEST_APP1_NAME,
+                this::setHomeProcess,
+                TEST_APP2_PID, TEST_APP2_UID, PROCESS_STATE_HOME, HOME_APP_ADJ,
+                PROCESS_CAPABILITY_CPU_TIME, TEST_APP2_NAME, TEST_SERVICE2_NAME,
+                this::setHomeProcess,
+                BIND_AUTO_CREATE,
+                never(), atLeastOnce());
+    }
+
+    @Test
     @RequiresFlagsDisabled(com.android.server.am.Flags.FLAG_UNFREEZE_BIND_POLICY_FIX)
     public void testServiceDistinctBindingOomAdjWaivePriority() throws Exception {
         // Enable the flags.
@@ -624,6 +674,9 @@
         // Enable the flags.
         mSetFlagsRule.enableFlags(Flags.FLAG_SERVICE_BINDING_OOM_ADJ_POLICY);
 
+        // Note that some capabilities like PROCESS_CAPABILITY_CPU_TIME are special and propagated
+        // regardless of BIND_INCLUDE_CAPABILITIES. We don't test for them here.
+
         // Verify that there should be 0 oom adj update
         // because we didn't specify the "BIND_INCLUDE_CAPABILITIES"
         performTestServiceDistinctBindingOomAdj(TEST_APP1_PID, TEST_APP1_UID,
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 3b6c86e..0c92c10 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/JobParametersTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/JobParametersTest.java
@@ -29,15 +29,20 @@
 import android.app.job.JobParameters;
 import android.net.Uri;
 import android.os.Parcel;
-import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.annotations.EnableFlags;
 import android.platform.test.flag.junit.CheckFlagsRule;
 import android.platform.test.flag.junit.DeviceFlagsValueProvider;
 import android.platform.test.flag.junit.SetFlagsRule;
 
+import libcore.junit.util.compat.CoreCompatChangeRule;
+import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
+import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.TestRule;
 import org.mockito.Mock;
 import org.mockito.MockitoSession;
 import org.mockito.quality.Strictness;
@@ -47,7 +52,10 @@
     private static final int TEST_JOB_ID_1 = 123;
     private static final String TEST_NAMESPACE = "TEST_NAMESPACE";
     private static final String TEST_DEBUG_STOP_REASON = "TEST_DEBUG_STOP_REASON";
-    @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+    @Rule
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+    @Rule
+    public TestRule compatChangeRule = new CoreCompatChangeRule();
 
     @Rule
     public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
@@ -129,9 +137,10 @@
     }
 
     /** Test to verify that the JobParameters Cleaner is disabled */
-    @RequiresFlagsEnabled(FLAG_HANDLE_ABANDONED_JOBS)
     @Test
-    public void testCleanerWithLeakedJobCleanerDisabled_flagHandleAbandonedJobs() {
+    @EnableFlags(FLAG_HANDLE_ABANDONED_JOBS)
+    @DisableCompatChanges({JobParameters.OVERRIDE_HANDLE_ABANDONED_JOBS})
+    public void testCleanerWithLeakedNoJobCleaner_EnableFlagDisableCompatHandleAbandonedJobs() {
         // Inject real JobCallbackCleanup
         JobParameters jobParameters = JobParameters.CREATOR.createFromParcel(mMockParcel);
 
@@ -150,4 +159,31 @@
         assertThat(jobParameters.getCleanable()).isNull();
         assertThat(jobParameters.getJobCleanupCallback()).isNull();
     }
+
+    /**
+     * Test to verify that the JobParameters Cleaner is not enabled
+     * when the compat change is enabled and the flag is enabled
+     */
+    @Test
+    @EnableFlags(FLAG_HANDLE_ABANDONED_JOBS)
+    @EnableCompatChanges({JobParameters.OVERRIDE_HANDLE_ABANDONED_JOBS})
+    public void testCleanerWithLeakedNoJobCleaner_EnableFlagEnableCompatHandleAbandonedJobs() {
+        // Inject real JobCallbackCleanup
+        JobParameters jobParameters = JobParameters.CREATOR.createFromParcel(mMockParcel);
+
+        // Enable the cleaner
+        jobParameters.enableCleaner();
+
+        // Verify the cleaner is not enabled
+        assertThat(jobParameters.getCleanable()).isNull();
+        assertThat(jobParameters.getJobCleanupCallback()).isNull();
+
+        // Disable the cleaner
+        jobParameters.disableCleaner();
+
+        // Verify the cleaner is disabled
+        assertThat(jobParameters.getCleanable()).isNull();
+        assertThat(jobParameters.getJobCleanupCallback()).isNull();
+    }
+
 }
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
index c831475..8c09f26 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
@@ -53,6 +53,7 @@
 
 import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
+import android.app.AppGlobals;
 import android.app.IActivityManager;
 import android.app.UiModeManager;
 import android.app.job.JobInfo;
@@ -60,6 +61,7 @@
 import android.app.job.JobScheduler;
 import android.app.job.JobWorkItem;
 import android.app.usage.UsageStatsManagerInternal;
+import android.compat.testing.PlatformCompatChangeRule;
 import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -79,10 +81,10 @@
 import android.os.Looper;
 import android.os.Process;
 import android.os.RemoteException;
-import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.WorkSource;
 import android.os.WorkSource.WorkChain;
+import android.platform.test.annotations.DisableFlags;
 import android.platform.test.annotations.EnableFlags;
 import android.platform.test.annotations.RequiresFlagsDisabled;
 import android.platform.test.annotations.RequiresFlagsEnabled;
@@ -104,10 +106,14 @@
 import com.android.server.pm.UserManagerInternal;
 import com.android.server.usage.AppStandbyInternal;
 
+import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
+import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.TestRule;
 import org.mockito.ArgumentCaptor;
 import org.mockito.ArgumentMatchers;
 import org.mockito.Mock;
@@ -119,6 +125,7 @@
 import java.time.ZoneOffset;
 
 public class JobSchedulerServiceTest {
+    private static final String SOURCE_PACKAGE = "com.android.frameworks.mockingservicestests";
     private static final String TAG = JobSchedulerServiceTest.class.getSimpleName();
     private static final int TEST_UID = 10123;
 
@@ -140,8 +147,13 @@
     @Rule
     public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
 
+    @Rule
+    public TestRule compatChangeRule = new PlatformCompatChangeRule();
+
     private ChargingPolicyChangeListener mChargingPolicyChangeListener;
 
+    private int mSourceUid;
+
     private class TestJobSchedulerService extends JobSchedulerService {
         TestJobSchedulerService(Context context) {
             super(context);
@@ -156,7 +168,6 @@
                 .strictness(Strictness.LENIENT)
                 .mockStatic(LocalServices.class)
                 .mockStatic(PermissionChecker.class)
-                .mockStatic(ServiceManager.class)
                 .startMocking();
 
         // Called in JobSchedulerService constructor.
@@ -225,6 +236,7 @@
         verify(mBatteryManagerInternal).registerChargingPolicyChangeListener(
                 chargingPolicyChangeListenerCaptor.capture());
         mChargingPolicyChangeListener = chargingPolicyChangeListenerCaptor.getValue();
+        mSourceUid = AppGlobals.getPackageManager().getPackageUid(SOURCE_PACKAGE, 0, 0);
     }
 
     @After
@@ -1062,6 +1074,7 @@
      */
     @Test
     @EnableFlags(FLAG_HANDLE_ABANDONED_JOBS)
+    @DisableCompatChanges({JobParameters.OVERRIDE_HANDLE_ABANDONED_JOBS})
     public void testGetRescheduleJobForFailure_abandonedJob() {
         final long nowElapsed = sElapsedRealtimeClock.millis();
         final long initialBackoffMs = MINUTE_IN_MILLIS;
@@ -1073,6 +1086,9 @@
         assertEquals(JobStatus.NO_EARLIEST_RUNTIME, originalJob.getEarliestRunTime());
         assertEquals(JobStatus.NO_LATEST_RUNTIME, originalJob.getLatestRunTimeElapsed());
 
+        spyOn(originalJob);
+        doReturn(mSourceUid).when(originalJob).getSourceUid();
+
         // failure = 1, systemStop = 0, abandoned = 1
         JobStatus rescheduledJob = mService.getRescheduleJobForFailureLocked(originalJob,
                 JobParameters.STOP_REASON_DEVICE_STATE,
@@ -1080,6 +1096,8 @@
         assertEquals(nowElapsed + initialBackoffMs, rescheduledJob.getEarliestRunTime());
         assertEquals(JobStatus.NO_LATEST_RUNTIME, rescheduledJob.getLatestRunTimeElapsed());
 
+        spyOn(rescheduledJob);
+        doReturn(mSourceUid).when(rescheduledJob).getSourceUid();
         // failure = 2, systemstop = 0, abandoned = 2
         rescheduledJob = mService.getRescheduleJobForFailureLocked(rescheduledJob,
                 JobParameters.STOP_REASON_DEVICE_STATE,
@@ -1125,6 +1143,44 @@
     }
 
     /**
+     * Confirm that {@link JobSchedulerService#shouldUseAggressiveBackoff(int, int)} returns true
+     * when the number of abandoned jobs is greater than the threshold.
+     */
+    @Test
+    @EnableFlags(FLAG_HANDLE_ABANDONED_JOBS)
+    @DisableCompatChanges({JobParameters.OVERRIDE_HANDLE_ABANDONED_JOBS})
+    public void testGetRescheduleJobForFailure_EnableFlagDisableCompatCheckAggressiveBackoff() {
+        assertFalse(mService.shouldUseAggressiveBackoff(
+                        mService.mConstants.ABANDONED_JOB_TIMEOUTS_BEFORE_AGGRESSIVE_BACKOFF - 1,
+                        mSourceUid));
+        assertFalse(mService.shouldUseAggressiveBackoff(
+                        mService.mConstants.ABANDONED_JOB_TIMEOUTS_BEFORE_AGGRESSIVE_BACKOFF,
+                        mSourceUid));
+        assertTrue(mService.shouldUseAggressiveBackoff(
+                        mService.mConstants.ABANDONED_JOB_TIMEOUTS_BEFORE_AGGRESSIVE_BACKOFF + 1,
+                        mSourceUid));
+    }
+
+    /**
+     * Confirm that {@link JobSchedulerService#shouldUseAggressiveBackoff(int, int)} returns false
+     * always when the compat change is enabled and the flag is enabled.
+     */
+    @Test
+    @EnableFlags(FLAG_HANDLE_ABANDONED_JOBS)
+    @EnableCompatChanges({JobParameters.OVERRIDE_HANDLE_ABANDONED_JOBS})
+    public void testGetRescheduleJobForFailure_EnableFlagEnableCompatCheckAggressiveBackoff() {
+        assertFalse(mService.shouldUseAggressiveBackoff(
+                        mService.mConstants.ABANDONED_JOB_TIMEOUTS_BEFORE_AGGRESSIVE_BACKOFF - 1,
+                        mSourceUid));
+        assertFalse(mService.shouldUseAggressiveBackoff(
+                        mService.mConstants.ABANDONED_JOB_TIMEOUTS_BEFORE_AGGRESSIVE_BACKOFF,
+                        mSourceUid));
+        assertFalse(mService.shouldUseAggressiveBackoff(
+                        mService.mConstants.ABANDONED_JOB_TIMEOUTS_BEFORE_AGGRESSIVE_BACKOFF + 1,
+                        mSourceUid));
+    }
+
+    /**
      * Confirm that
      * {@link JobSchedulerService#getRescheduleJobForFailureLocked(JobStatus, int, int)}
      * returns a job that is correctly marked as demoted by the user.
@@ -2319,11 +2375,12 @@
     }
 
     /**
-     * Tests that jobs scheduled through a proxy (eg. system server) don't count towards scheduling
+     * Tests that jobs scheduled through a proxy (eg. system server) count towards scheduling
      * limits.
      */
     @Test
-    public void testScheduleLimiting_Proxy() {
+    @DisableFlags(Flags.FLAG_ENFORCE_SCHEDULE_LIMIT_TO_PROXY_JOBS)
+    public void testScheduleLimiting_Proxy_NotCountTowardsLimit() {
         mService.mConstants.ENABLE_API_QUOTAS = true;
         mService.mConstants.API_QUOTA_SCHEDULE_COUNT = 300;
         mService.mConstants.API_QUOTA_SCHEDULE_WINDOW_MS = 300000;
@@ -2342,6 +2399,32 @@
     }
 
     /**
+     * Tests that jobs scheduled through a proxy (eg. system server) don't count towards scheduling
+     * limits.
+     */
+    @Test
+    @EnableFlags(Flags.FLAG_ENFORCE_SCHEDULE_LIMIT_TO_PROXY_JOBS)
+    public void testScheduleLimiting_Proxy_CountTowardsLimit() {
+        mService.mConstants.ENABLE_API_QUOTAS = true;
+        mService.mConstants.API_QUOTA_SCHEDULE_COUNT = 300;
+        mService.mConstants.API_QUOTA_SCHEDULE_WINDOW_MS = 300000;
+        mService.mConstants.API_QUOTA_SCHEDULE_THROW_EXCEPTION = false;
+        mService.mConstants.API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT = true;
+        mService.updateQuotaTracker();
+        mService.resetScheduleQuota();
+
+        final JobInfo job = createJobInfo().setPersisted(true).build();
+        for (int i = 0; i < 500; ++i) {
+            final int expected =
+                    i < 300 ? JobScheduler.RESULT_SUCCESS : JobScheduler.RESULT_FAILURE;
+            assertEquals("Got unexpected result for schedule #" + (i + 1),
+                    expected,
+                    mService.scheduleAsPackage(job, null, TEST_UID, "proxied.package", 0, "JSSTest",
+                            ""));
+        }
+    }
+
+    /**
      * Tests that jobs scheduled by an app for itself as if through a proxy are counted towards
      * scheduling limits.
      */
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/JobServiceContextTest.java b/services/tests/mockingservicestests/src/com/android/server/job/JobServiceContextTest.java
index 8c66fd0..904545b 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/JobServiceContextTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/JobServiceContextTest.java
@@ -31,6 +31,7 @@
 
 import android.app.AppGlobals;
 import android.app.job.JobParameters;
+import android.compat.testing.PlatformCompatChangeRule;
 import android.content.Context;
 import android.os.Looper;
 import android.os.PowerManager;
@@ -43,11 +44,15 @@
 import com.android.server.job.JobServiceContext.JobCallback;
 import com.android.server.job.controllers.JobStatus;
 
+import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
+import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.ClassRule;
 import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.TestRule;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.MockitoSession;
@@ -58,11 +63,14 @@
 import java.time.ZoneOffset;
 
 public class JobServiceContextTest {
+    private static final String SOURCE_PACKAGE = "com.android.frameworks.mockingservicestests";
     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();
+    @Rule
+    public TestRule compatChangeRule = new PlatformCompatChangeRule();
     @Mock
     private JobSchedulerService mMockJobSchedulerService;
     @Mock
@@ -86,13 +94,13 @@
     private MockitoSession mMockingSession;
     private JobServiceContext mJobServiceContext;
     private Object mLock;
+    private int mSourceUid;
 
     @Before
     public void setUp() throws Exception {
         mMockingSession =
                 mockitoSession()
                         .initMocks(this)
-                        .mockStatic(AppGlobals.class)
                         .strictness(Strictness.LENIENT)
                         .startMocking();
         JobSchedulerService.sElapsedRealtimeClock =
@@ -111,6 +119,7 @@
                         mMockLooper);
         spyOn(mJobServiceContext);
         mJobServiceContext.setJobParamsLockedForTest(mMockJobParameters);
+        mSourceUid = AppGlobals.getPackageManager().getPackageUid(SOURCE_PACKAGE, 0, 0);
     }
 
     @After
@@ -130,11 +139,14 @@
     }
 
     /**
-     * Test that Abandoned jobs that are timed out are stopped with the correct stop reason
+     * Test that with the compat change disabled and the flag enabled, abandoned
+     * jobs that are timed out are stopped with the correct stop reason and the
+     * job is marked as abandoned.
      */
     @Test
     @EnableFlags(FLAG_HANDLE_ABANDONED_JOBS)
-    public void testJobServiceContext_TimeoutAbandonedJob() {
+    @DisableCompatChanges({JobParameters.OVERRIDE_HANDLE_ABANDONED_JOBS})
+    public void testJobServiceContext_TimeoutAbandonedJob_EnableFlagDisableCompat() {
         mJobServiceContext.mVerb = JobServiceContext.VERB_EXECUTING;
         ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
         doNothing().when(mJobServiceContext).sendStopMessageLocked(captor.capture());
@@ -143,6 +155,7 @@
         mJobServiceContext.setPendingStopReasonLockedForTest(JobParameters.STOP_REASON_UNDEFINED);
 
         mJobServiceContext.setRunningJobLockedForTest(mMockJobStatus);
+        doReturn(mSourceUid).when(mMockJobStatus).getSourceUid();
         doReturn(true).when(mMockJobStatus).isAbandoned();
         mJobServiceContext.mVerb = JobServiceContext.VERB_EXECUTING;
 
@@ -158,11 +171,14 @@
     }
 
     /**
-     * Test that non-abandoned jobs that are timed out are stopped with the correct stop reason
+     * Test that with the compat change enabled and the flag enabled, abandoned
+     * jobs that are timed out are stopped with the correct stop reason and the
+     * job is not marked as abandoned.
      */
     @Test
     @EnableFlags(FLAG_HANDLE_ABANDONED_JOBS)
-    public void testJobServiceContext_TimeoutNoAbandonedJob() {
+    @EnableCompatChanges({JobParameters.OVERRIDE_HANDLE_ABANDONED_JOBS})
+    public void testJobServiceContext_TimeoutAbandonedJob_EnableFlagEnableCompat() {
         mJobServiceContext.mVerb = JobServiceContext.VERB_EXECUTING;
         ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
         doNothing().when(mJobServiceContext).sendStopMessageLocked(captor.capture());
@@ -171,7 +187,8 @@
         mJobServiceContext.setPendingStopReasonLockedForTest(JobParameters.STOP_REASON_UNDEFINED);
 
         mJobServiceContext.setRunningJobLockedForTest(mMockJobStatus);
-        doReturn(false).when(mMockJobStatus).isAbandoned();
+        doReturn(mSourceUid).when(mMockJobStatus).getSourceUid();
+        doReturn(true).when(mMockJobStatus).isAbandoned();
         mJobServiceContext.mVerb = JobServiceContext.VERB_EXECUTING;
 
         mJobServiceContext.handleOpTimeoutLocked();
@@ -186,12 +203,14 @@
     }
 
     /**
-     * Test that abandoned jobs that are timed out while the flag is disabled
-     * are stopped with the correct stop reason
+     * Test that with the compat change disabled and the flag disabled, abandoned
+     * jobs that are timed out are stopped with the correct stop reason and the
+     * job is not marked as abandoned.
      */
     @Test
     @DisableFlags(FLAG_HANDLE_ABANDONED_JOBS)
-    public void testJobServiceContext_TimeoutAbandonedJob_flagHandleAbandonedJobsDisabled() {
+    @DisableCompatChanges({JobParameters.OVERRIDE_HANDLE_ABANDONED_JOBS})
+    public void testJobServiceContext_TimeoutAbandonedJob_DisableFlagDisableCompat() {
         mJobServiceContext.mVerb = JobServiceContext.VERB_EXECUTING;
         ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
         doNothing().when(mJobServiceContext).sendStopMessageLocked(captor.capture());
@@ -201,6 +220,39 @@
 
         mJobServiceContext.setRunningJobLockedForTest(mMockJobStatus);
         doReturn(true).when(mMockJobStatus).isAbandoned();
+        doReturn(mSourceUid).when(mMockJobStatus).getSourceUid();
+        mJobServiceContext.mVerb = JobServiceContext.VERB_EXECUTING;
+
+        synchronized (mLock) {
+            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 non-abandoned jobs that are timed out are stopped with the correct stop reason
+     */
+    @Test
+    @EnableFlags(FLAG_HANDLE_ABANDONED_JOBS)
+    @DisableCompatChanges({JobParameters.OVERRIDE_HANDLE_ABANDONED_JOBS})
+    public void testJobServiceContext_TimeoutNoAbandonedJob() {
+        mJobServiceContext.mVerb = JobServiceContext.VERB_EXECUTING;
+        ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
+        synchronized (mLock) {
+            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();
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/fudger/LocationFudgerCacheTest.java b/services/tests/mockingservicestests/src/com/android/server/location/fudger/LocationFudgerCacheTest.java
index 6b7eda2..c89048a 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/fudger/LocationFudgerCacheTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/fudger/LocationFudgerCacheTest.java
@@ -280,6 +280,51 @@
                 eq(POINT_IN_TIMES_SQUARE[1]), eq(numAdditionalCells), any());
     }
 
+    @Test
+    public void fetchDefaultCoarseningLevelIfNeeded_withDefaultValue_doesNotQueryProvider()
+            throws RemoteException {
+        // Arrange.
+        ProxyPopulationDensityProvider provider = mock(ProxyPopulationDensityProvider.class);
+        LocationFudgerCache cache = new LocationFudgerCache(provider);
+
+        ArgumentCaptor<IS2LevelCallback> argumentCaptor = ArgumentCaptor.forClass(
+                IS2LevelCallback.class);
+        verify(provider, times(1)).getDefaultCoarseningLevel(argumentCaptor.capture());
+
+        IS2LevelCallback cb = argumentCaptor.getValue();
+        cb.onResult(10);
+
+        assertThat(cache.hasDefaultValue()).isTrue();
+
+        // Act.
+        cache.fetchDefaultCoarseningLevelIfNeeded();
+
+        // Assert. The method is not called again.
+        verify(provider, times(1)).getDefaultCoarseningLevel(any());
+    }
+
+    @Test
+    public void fetchDefaultCoarseningLevelIfNeeded_withoutDefaultValue_doesQueryProvider()
+            throws RemoteException {
+        // Arrange.
+        ProxyPopulationDensityProvider provider = mock(ProxyPopulationDensityProvider.class);
+        LocationFudgerCache cache = new LocationFudgerCache(provider);
+
+        ArgumentCaptor<IS2LevelCallback> argumentCaptor = ArgumentCaptor.forClass(
+                IS2LevelCallback.class);
+        verify(provider, times(1)).getDefaultCoarseningLevel(argumentCaptor.capture());
+
+        IS2LevelCallback cb = argumentCaptor.getValue();
+        cb.onError();
+
+        assertThat(cache.hasDefaultValue()).isFalse();
+
+        // Act.
+        cache.fetchDefaultCoarseningLevelIfNeeded();
+
+        // Assert. The method is called again.
+        verify(provider, times(2)).getDefaultCoarseningLevel(any());
+    }
 
     @Test
     public void locationFudgerCache_canContainUpToMaxSizeItems() {
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/fudger/LocationFudgerTest.java b/services/tests/mockingservicestests/src/com/android/server/location/fudger/LocationFudgerTest.java
index 835705d..2e4652e 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/fudger/LocationFudgerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/fudger/LocationFudgerTest.java
@@ -176,8 +176,28 @@
     }
 
     @Test
-    public void testDensityBasedCoarsening_ifFeatureIsDisabled_cacheIsNotUsed() {
+    public void testDensityBasedCoarsening_ifAnyFlagIsOff1_cacheIsNotUsed() {
+        // This feature requires two flags: one for the population density provider (which could
+        // be used by various client), and a second one for actually enabling the new coarsening
+        // algorithm.
         mSetFlagsRule.disableFlags(Flags.FLAG_DENSITY_BASED_COARSE_LOCATIONS);
+        mSetFlagsRule.enableFlags(Flags.FLAG_POPULATION_DENSITY_PROVIDER);
+        LocationFudgerCache cache = mock(LocationFudgerCache.class);
+
+        mFudger.setLocationFudgerCache(cache);
+
+        mFudger.createCoarse(createLocation("test", mRandom));
+
+        verify(cache, never()).getCoarseningLevel(anyDouble(), anyDouble());
+    }
+
+    @Test
+    public void testDensityBasedCoarsening_ifAnyFlagIsOff2_cacheIsNotUsed() {
+        // This feature requires two flags: one for the population density provider (which could
+        // be used by various client), and a second one for actually enabling the new coarsening
+        // algorithm.
+        mSetFlagsRule.enableFlags(Flags.FLAG_DENSITY_BASED_COARSE_LOCATIONS);
+        mSetFlagsRule.disableFlags(Flags.FLAG_POPULATION_DENSITY_PROVIDER);
         LocationFudgerCache cache = mock(LocationFudgerCache.class);
 
         mFudger.setLocationFudgerCache(cache);
@@ -190,6 +210,7 @@
     @Test
     public void testDensityBasedCoarsening_ifFeatureIsEnabledButNoDefaultValue_cacheIsNotUsed() {
         mSetFlagsRule.enableFlags(Flags.FLAG_DENSITY_BASED_COARSE_LOCATIONS);
+        mSetFlagsRule.enableFlags(Flags.FLAG_POPULATION_DENSITY_PROVIDER);
         LocationFudgerCache cache = mock(LocationFudgerCache.class);
         doReturn(false).when(cache).hasDefaultValue();
 
@@ -201,8 +222,23 @@
     }
 
     @Test
+    public void testDensityBasedCoarsening_ifFeatureIsEnabledButNoDefaultValue_defaultIsFetched() {
+        mSetFlagsRule.enableFlags(Flags.FLAG_DENSITY_BASED_COARSE_LOCATIONS);
+        mSetFlagsRule.enableFlags(Flags.FLAG_POPULATION_DENSITY_PROVIDER);
+        LocationFudgerCache cache = mock(LocationFudgerCache.class);
+        doReturn(false).when(cache).hasDefaultValue();
+
+        mFudger.setLocationFudgerCache(cache);
+
+        mFudger.createCoarse(createLocation("test", mRandom));
+
+        verify(cache).fetchDefaultCoarseningLevelIfNeeded();
+    }
+
+    @Test
     public void testDensityBasedCoarsening_ifFeatureIsEnabledAndDefaultIsSet_cacheIsUsed() {
         mSetFlagsRule.enableFlags(Flags.FLAG_DENSITY_BASED_COARSE_LOCATIONS);
+        mSetFlagsRule.enableFlags(Flags.FLAG_POPULATION_DENSITY_PROVIDER);
         LocationFudgerCache cache = mock(LocationFudgerCache.class);
         doReturn(true).when(cache).hasDefaultValue();
 
@@ -223,6 +259,7 @@
         // location/geometry/S2CellIdUtilsTest.java
 
         mSetFlagsRule.enableFlags(Flags.FLAG_DENSITY_BASED_COARSE_LOCATIONS);
+        mSetFlagsRule.enableFlags(Flags.FLAG_POPULATION_DENSITY_PROVIDER);
         // Arbitrary location in Times Square, NYC
         double[] latLng = new double[] {40.758896, -73.985130};
         int s2Level = 1;
diff --git a/services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java b/services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java
index b166514..4b2e850 100644
--- a/services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java
+++ b/services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java
@@ -188,6 +188,7 @@
         mSupportInfo.headroom.cpuMinIntervalMillis = 2000;
         mSupportInfo.headroom.isGpuSupported = true;
         mSupportInfo.headroom.gpuMinIntervalMillis = 2000;
+        mSupportInfo.compositionData = new SupportInfo.CompositionDataSupportInfo();
         return mSupportInfo;
     }
 
@@ -1274,6 +1275,53 @@
     }
 
     @Test
+    public void testCpuHeadroomInvalidParams() {
+        HintManagerService service = createService();
+        final CpuHeadroomParamsInternal param1 = new CpuHeadroomParamsInternal();
+        param1.calculationType = 100;
+        assertThrows(IllegalArgumentException.class, () -> {
+            service.getBinderServiceInstance().getCpuHeadroom(param1);
+        });
+
+        final CpuHeadroomParamsInternal param2 = new CpuHeadroomParamsInternal();
+        param2.calculationWindowMillis = 49;
+        assertThrows(IllegalArgumentException.class, () -> {
+            service.getBinderServiceInstance().getCpuHeadroom(param2);
+        });
+        param2.calculationWindowMillis = 10001;
+        assertThrows(IllegalArgumentException.class, () -> {
+            service.getBinderServiceInstance().getCpuHeadroom(param2);
+        });
+
+        final CpuHeadroomParamsInternal param3 = new CpuHeadroomParamsInternal();
+        param3.tids = new int[]{1, 2, 3, 4, 5, 6};
+        assertThrows(IllegalArgumentException.class, () -> {
+            service.getBinderServiceInstance().getCpuHeadroom(param3);
+        });
+    }
+
+    @Test
+    public void testGpuHeadroomInvalidParams() {
+        HintManagerService service = createService();
+        final GpuHeadroomParamsInternal param1 = new GpuHeadroomParamsInternal();
+        param1.calculationType = 100;
+        assertThrows(IllegalArgumentException.class, () -> {
+            service.getBinderServiceInstance().getGpuHeadroom(param1);
+        });
+
+        final GpuHeadroomParamsInternal param2 = new GpuHeadroomParamsInternal();
+        param2.calculationWindowMillis = 49;
+        assertThrows(IllegalArgumentException.class, () -> {
+            service.getBinderServiceInstance().getGpuHeadroom(param2);
+        });
+        param2.calculationWindowMillis = 10001;
+        assertThrows(IllegalArgumentException.class, () -> {
+            service.getBinderServiceInstance().getGpuHeadroom(param2);
+        });
+    }
+
+
+    @Test
     public void testCpuHeadroomCache() throws Exception {
         CpuHeadroomParamsInternal params1 = new CpuHeadroomParamsInternal();
         CpuHeadroomParams halParams1 = new CpuHeadroomParams();
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java
index b67ec8b..bc81feb 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java
@@ -68,6 +68,7 @@
 @RunWith(AndroidJUnit4.class)
 public class BatteryStatsHistoryTest {
     private static final String TAG = "BatteryStatsHistoryTest";
+    private static final int MAX_HISTORY_BUFFER_SIZE = 1024;
     private final Parcel mHistoryBuffer = Parcel.obtain();
     private File mSystemDir;
     private File mHistoryDir;
@@ -98,8 +99,9 @@
 
         mClock.realtime = 123;
 
-        mHistory = new BatteryStatsHistory(mHistoryBuffer, mSystemDir, 32, 1024,
-                mStepDetailsCalculator, mClock, mMonotonicClock, mTracer, mEventLogger);
+        mHistory = new BatteryStatsHistory(mHistoryBuffer, mSystemDir, 32768,
+                MAX_HISTORY_BUFFER_SIZE, mStepDetailsCalculator, mClock, mMonotonicClock, mTracer,
+                mEventLogger);
 
         when(mStepDetailsCalculator.getHistoryStepDetails())
                 .thenReturn(new BatteryStats.HistoryStepDetails());
@@ -196,12 +198,15 @@
     }
 
     @Test
-    public void testStartNextFile() {
+    public void testStartNextFile() throws Exception {
+        mHistory.forceRecordAllHistory();
+
         mClock.realtime = 123;
 
         List<String> fileList = new ArrayList<>();
         fileList.add("123.bh");
         createActiveFile(mHistory);
+        fillActiveFile(mHistory);
 
         // create file 1 to 31.
         for (int i = 1; i < 32; i++) {
@@ -210,6 +215,8 @@
 
             mHistory.startNextFile(mClock.realtime);
             createActiveFile(mHistory);
+            fillActiveFile(mHistory);
+
             verifyFileNames(mHistory, fileList);
             verifyActiveFile(mHistory, mClock.realtime + ".bh");
         }
@@ -225,6 +232,8 @@
         verifyFileNames(mHistory, fileList);
         verifyActiveFile(mHistory, "32000.bh");
 
+        fillActiveFile(mHistory);
+
         // create file 33
         mClock.realtime = 1000 * 33;
         mHistory.startNextFile(mClock.realtime);
@@ -401,6 +410,14 @@
         }
     }
 
+    private void fillActiveFile(BatteryStatsHistory history) {
+        // Create roughly 1K of history
+        int initialSize = history.getHistoryUsedSize();
+        while (history.getHistoryUsedSize() < initialSize + 1000) {
+            history.recordCurrentTimeChange(mClock.realtime, mClock.uptime, 0xFFFFFFFFL);
+        }
+    }
+
     @Test
     public void recordPowerStats() {
         PowerStats.Descriptor descriptor = new PowerStats.Descriptor(42, "foo", 1, null, 0, 2,
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java
index 709f83b..73dcfe7 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java
@@ -36,6 +36,7 @@
 import android.os.BatteryUsageStats;
 import android.os.BatteryUsageStatsQuery;
 import android.os.ConditionVariable;
+import android.os.Handler;
 import android.os.Parcel;
 import android.os.Process;
 import android.os.UidBatteryConsumer;
@@ -81,8 +82,9 @@
                     .setAveragePower(PowerProfile.POWER_BATTERY_CAPACITY, 4000.0);
 
     private MockClock mMockClock = mStatsRule.getMockClock();
-    private MonotonicClock mMonotonicClock = new MonotonicClock(666777, mMockClock);
+    private MonotonicClock mMonotonicClock = mStatsRule.getMonotonicClock();
     private Context mContext;
+    private PowerStatsStore mPowerStatsStore;
 
     @Before
     public void setup() throws IOException {
@@ -93,6 +95,9 @@
         } else {
             mContext = InstrumentationRegistry.getContext();
         }
+        mPowerStatsStore = spy(new PowerStatsStore(
+                new File(mStatsRule.getHistoryDir(), getClass().getSimpleName()),
+                mStatsRule.getHandler()));
     }
 
     @Test
@@ -274,10 +279,7 @@
         powerAttributor.setPowerComponentSupported(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT,
                 true);
 
-        BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(mContext,
-                powerAttributor, mStatsRule.getPowerProfile(),
-                mStatsRule.getCpuScalingPolicies(), mock(PowerStatsStore.class), 0, mMockClock,
-                mMonotonicClock);
+        BatteryUsageStatsProvider provider = createBatteryUsageStatsProvider(0);
 
         return provider.getBatteryUsageStats(batteryStats, BatteryUsageStatsQuery.DEFAULT);
     }
@@ -331,30 +333,30 @@
         BatteryStats.HistoryItem item;
 
         assertThat(item = iterator.next()).isNotNull();
-        assertHistoryItem(item,
+        assertHistoryItem(batteryStats, item,
                 BatteryStats.HistoryItem.CMD_RESET, BatteryStats.HistoryItem.EVENT_NONE,
                 null, 0, 3_600_000, 90, 1_000_000);
 
         assertThat(item = iterator.next()).isNotNull();
-        assertHistoryItem(item,
+        assertHistoryItem(batteryStats, item,
                 BatteryStats.HistoryItem.CMD_UPDATE, BatteryStats.HistoryItem.EVENT_NONE,
                 null, 0, 3_600_000, 90, 1_000_000);
         assertThat(item.states & BatteryStats.HistoryItem.STATE_CPU_RUNNING_FLAG).isNotEqualTo(0);
 
         assertThat(item = iterator.next()).isNotNull();
-        assertHistoryItem(item,
+        assertHistoryItem(batteryStats, item,
                 BatteryStats.HistoryItem.CMD_UPDATE, BatteryStats.HistoryItem.EVENT_NONE,
                 null, 0, 3_600_000, 90, 2_000_000);
         assertThat(item.states & BatteryStats.HistoryItem.STATE_CPU_RUNNING_FLAG).isEqualTo(0);
 
         assertThat(item = iterator.next()).isNotNull();
-        assertHistoryItem(item,
+        assertHistoryItem(batteryStats, item,
                 BatteryStats.HistoryItem.CMD_UPDATE,
                 BatteryStats.HistoryItem.EVENT_ALARM | BatteryStats.HistoryItem.EVENT_FLAG_START,
                 "foo", APP_UID, 3_600_000, 90, 3_000_000);
 
         assertThat(item = iterator.next()).isNotNull();
-        assertHistoryItem(item,
+        assertHistoryItem(batteryStats, item,
                 BatteryStats.HistoryItem.CMD_UPDATE,
                 BatteryStats.HistoryItem.EVENT_ALARM | BatteryStats.HistoryItem.EVENT_FLAG_FINISH,
                 "foo", APP_UID, 3_600_000, 90, 3_001_000);
@@ -441,14 +443,15 @@
             assertThat(item.eventTag.string).startsWith(uid + " ");
             assertThat(item.batteryChargeUah).isEqualTo(3_600_000);
             assertThat(item.batteryLevel).isEqualTo(90);
-            assertThat(item.time).isEqualTo((long) 1_000_000);
+            assertThat(item.time).isEqualTo(batteryStats.getMonotonicStartTime() + 1_000_000);
         }
 
         assertThat(expectedUid).isEqualTo(200);
     }
 
-    private void assertHistoryItem(BatteryStats.HistoryItem item, int command, int eventCode,
-            String tag, int uid, int batteryChargeUah, int batteryLevel, long elapsedTimeMs) {
+    private void assertHistoryItem(MockBatteryStatsImpl batteryStats, BatteryStats.HistoryItem item,
+            int command, int eventCode, String tag, int uid, int batteryChargeUah, int batteryLevel,
+            long elapsedTimeMs) {
         assertThat(item.cmd).isEqualTo(command);
         assertThat(item.eventCode).isEqualTo(eventCode);
         if (tag == null) {
@@ -460,7 +463,7 @@
         assertThat(item.batteryChargeUah).isEqualTo(batteryChargeUah);
         assertThat(item.batteryLevel).isEqualTo(batteryLevel);
 
-        assertThat(item.time).isEqualTo(elapsedTimeMs);
+        assertThat(item.time).isEqualTo(batteryStats.getMonotonicStartTime() + elapsedTimeMs);
     }
 
     @Test
@@ -566,38 +569,66 @@
 
         assertThat(stats.getStatsStartTimestamp()).isEqualTo(5 * MINUTE_IN_MS);
         assertThat(stats.getStatsEndTimestamp()).isEqualTo(55 * MINUTE_IN_MS);
-        assertThat(stats.getAggregateBatteryConsumer(
-                        BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE)
-                .getConsumedPower(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT))
-                .isWithin(0.0001)
-                .of(180.0);  // 360 mA * 0.5 hours
-        assertThat(stats.getAggregateBatteryConsumer(
-                        BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE)
-                .getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT))
-                .isEqualTo((10 + 20) * MINUTE_IN_MS);
-        final UidBatteryConsumer uidBatteryConsumer = stats.getUidBatteryConsumers().stream()
-                .filter(uid -> uid.getUid() == APP_UID).findFirst().get();
-        assertThat(uidBatteryConsumer
-                .getConsumedPower(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT))
-                .isWithin(0.1)
-                .of(180.0);
+        assertBatteryConsumer(stats, 180.0, (10 + 20) * MINUTE_IN_MS);
+        assertBatteryConsumer(stats, APP_UID, 180.0, (10 + 20) * MINUTE_IN_MS);
 
         stats.close();
     }
 
     @Test
     public void accumulateBatteryUsageStats() throws Throwable {
-        accumulateBatteryUsageStats(10000000, 1);
+        MockBatteryStatsImpl batteryStats = mStatsRule.getBatteryStats();
+        accumulateBatteryUsageStats(batteryStats, 10000000, 0);
         // Accumulate every 200 bytes of battery history
-        accumulateBatteryUsageStats(200, 2);
-        accumulateBatteryUsageStats(50, 5);
+        accumulateBatteryUsageStats(batteryStats, 200, 2);
+        accumulateBatteryUsageStats(batteryStats, 50, 4);
         // Accumulate on every invocation of accumulateBatteryUsageStats
-        accumulateBatteryUsageStats(0, 7);
+        accumulateBatteryUsageStats(batteryStats, 0, 7);
     }
 
-    private void accumulateBatteryUsageStats(int accumulatedBatteryUsageStatsSpanSize,
-            int expectedNumberOfUpdates) throws Throwable {
-        BatteryStatsImpl batteryStats = spy(mStatsRule.getBatteryStats());
+    @Test
+    public void getAccumulatedBatteryUsageStats() throws Throwable {
+        MockBatteryStatsImpl batteryStats = mStatsRule.getBatteryStats();
+
+        // Only accumulate the first 25 minutes
+        accumulateBatteryUsageStats(batteryStats, 200, 1);
+
+        BatteryUsageStatsProvider batteryUsageStatsProvider = createBatteryUsageStatsProvider(200);
+
+        // At this point the last stored accumulated stats are `115 - 30 = 85` minutes old
+        BatteryUsageStats stats = batteryUsageStatsProvider.getBatteryUsageStats(batteryStats,
+                new BatteryUsageStatsQuery.Builder()
+                        .accumulated()
+                        .setMaxStatsAgeMs(90 * MINUTE_IN_MS)
+                        .build());
+
+        assertThat(stats.getStatsStartTimestamp()).isEqualTo(5 * MINUTE_IN_MS);
+        assertThat(stats.getStatsEndTimestamp()).isEqualTo(30 * MINUTE_IN_MS);
+        assertBatteryConsumer(stats, 60.0, 10 * MINUTE_IN_MS);
+        assertBatteryConsumer(stats, APP_UID, 60.0, 10 * MINUTE_IN_MS);
+
+        stats.close();
+
+        // Now force the usage stats to catch up to the current time
+        stats = batteryUsageStatsProvider.getBatteryUsageStats(batteryStats,
+                new BatteryUsageStatsQuery.Builder()
+                        .accumulated()
+                        .setMaxStatsAgeMs(5 * MINUTE_IN_MS)
+                        .build());
+
+        assertThat(stats.getStatsStartTimestamp()).isEqualTo(5 * MINUTE_IN_MS);
+        assertThat(stats.getStatsEndTimestamp()).isEqualTo(115 * MINUTE_IN_MS);
+        assertBatteryConsumer(stats, 360.0, 60 * MINUTE_IN_MS);
+        assertBatteryConsumer(stats, APP_UID, 360.0, 60 * MINUTE_IN_MS);
+
+        stats.close();
+    }
+
+    private void accumulateBatteryUsageStats(MockBatteryStatsImpl batteryStatsImpl,
+            int accumulatedBatteryUsageStatsSpanSize, int expectedNumberOfUpdates)
+            throws Throwable {
+        Handler handler = mStatsRule.getHandler();
+        MockBatteryStatsImpl batteryStats = spy(batteryStatsImpl);
         // Note - these two are in microseconds
         when(batteryStats.computeBatteryTimeRemaining(anyLong())).thenReturn(111_000L);
         when(batteryStats.computeChargeTimeRemaining(anyLong())).thenReturn(777_000L);
@@ -610,82 +641,76 @@
             batteryStats.resetAllStatsAndHistoryLocked(BatteryStatsImpl.RESET_REASON_ADB_COMMAND);
         }
 
-        PowerStatsStore powerStatsStore = spy(new PowerStatsStore(
-                new File(mStatsRule.getHistoryDir(), getClass().getSimpleName()),
-                mStatsRule.getHandler()));
-        powerStatsStore.reset();
+        mPowerStatsStore.reset();
 
         int[] count = new int[1];
         doAnswer(inv -> {
             count[0]++;
-            return null;
-        }).when(powerStatsStore).storePowerStatsSpan(any(PowerStatsSpan.class));
+            return inv.callRealMethod();
+        }).when(mPowerStatsStore).storePowerStatsSpan(any(PowerStatsSpan.class));
 
-        MultiStatePowerAttributor powerAttributor = new MultiStatePowerAttributor(mContext,
-                powerStatsStore, mStatsRule.getPowerProfile(), mStatsRule.getCpuScalingPolicies(),
-                () -> 3500);
-        for (int powerComponentId = 0; powerComponentId < BatteryConsumer.POWER_COMPONENT_COUNT;
-                powerComponentId++) {
-            powerAttributor.setPowerComponentSupported(powerComponentId, true);
-        }
-        powerAttributor.setPowerComponentSupported(BatteryConsumer.POWER_COMPONENT_ANY, true);
+        BatteryUsageStatsProvider batteryUsageStatsProvider = createBatteryUsageStatsProvider(
+                accumulatedBatteryUsageStatsSpanSize);
 
-        BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(mContext,
-                powerAttributor, mStatsRule.getPowerProfile(),
-                mStatsRule.getCpuScalingPolicies(), powerStatsStore,
-                accumulatedBatteryUsageStatsSpanSize, mMockClock, mMonotonicClock);
+        batteryUsageStatsProvider.accumulateBatteryUsageStatsAsync(batteryStats, handler);
 
-        provider.accumulateBatteryUsageStatsAsync(batteryStats, mStatsRule.getHandler());
-
+        setTime(10 * MINUTE_IN_MS);
         synchronized (batteryStats) {
             batteryStats.noteFlashlightOnLocked(APP_UID,
                     10 * MINUTE_IN_MS, 10 * MINUTE_IN_MS);
         }
 
-        provider.accumulateBatteryUsageStatsAsync(batteryStats, mStatsRule.getHandler());
+        batteryUsageStatsProvider.accumulateBatteryUsageStatsAsync(batteryStats, handler);
 
+        setTime(20 * MINUTE_IN_MS);
         synchronized (batteryStats) {
             batteryStats.noteFlashlightOffLocked(APP_UID,
                     20 * MINUTE_IN_MS, 20 * MINUTE_IN_MS);
         }
 
-        provider.accumulateBatteryUsageStatsAsync(batteryStats, mStatsRule.getHandler());
+        batteryUsageStatsProvider.accumulateBatteryUsageStatsAsync(batteryStats, handler);
 
+        setTime(30 * MINUTE_IN_MS);
         synchronized (batteryStats) {
             batteryStats.noteFlashlightOnLocked(APP_UID,
                     30 * MINUTE_IN_MS, 30 * MINUTE_IN_MS);
         }
 
-        provider.accumulateBatteryUsageStatsAsync(batteryStats, mStatsRule.getHandler());
+        batteryUsageStatsProvider.accumulateBatteryUsageStatsAsync(batteryStats, handler);
 
+        // Make sure the accumulated stats are computed and saved before generating more history
+        mStatsRule.waitForBackgroundThread();
+
+        setTime(50 * MINUTE_IN_MS);
         synchronized (batteryStats) {
             batteryStats.noteFlashlightOffLocked(APP_UID,
                     50 * MINUTE_IN_MS, 50 * MINUTE_IN_MS);
         }
         setTime(55 * MINUTE_IN_MS);
 
-        provider.accumulateBatteryUsageStatsAsync(batteryStats, mStatsRule.getHandler());
+        batteryUsageStatsProvider.accumulateBatteryUsageStatsAsync(batteryStats, handler);
 
         // This section has not been saved yet, but should be added to the accumulated totals
+        setTime(80 * MINUTE_IN_MS);
         synchronized (batteryStats) {
             batteryStats.noteFlashlightOnLocked(APP_UID,
                     80 * MINUTE_IN_MS, 80 * MINUTE_IN_MS);
         }
 
-        provider.accumulateBatteryUsageStatsAsync(batteryStats, mStatsRule.getHandler());
+        batteryUsageStatsProvider.accumulateBatteryUsageStatsAsync(batteryStats, handler);
 
+        setTime(110 * MINUTE_IN_MS);
         synchronized (batteryStats) {
             batteryStats.noteFlashlightOffLocked(APP_UID,
                     110 * MINUTE_IN_MS, 110 * MINUTE_IN_MS);
         }
         setTime(115 * MINUTE_IN_MS);
 
-        // Pick up the remainder of battery history that has not yet been accumulated
-        provider.accumulateBatteryUsageStats(batteryStats);
+        batteryUsageStatsProvider.accumulateBatteryUsageStatsAsync(batteryStats, handler);
 
         mStatsRule.waitForBackgroundThread();
 
-        BatteryUsageStats stats = provider.getBatteryUsageStats(batteryStats,
+        BatteryUsageStats stats = batteryUsageStatsProvider.getBatteryUsageStats(batteryStats,
                 new BatteryUsageStatsQuery.Builder().accumulated().build());
 
         assertThat(stats.getStatsStartTimestamp()).isEqualTo(5 * MINUTE_IN_MS);
@@ -696,29 +721,55 @@
         assertThat(stats.getBatteryCapacity()).isEqualTo(4000);  // from PowerProfile
 
         // Total: 10 + 20 + 30 = 60
-        assertThat(stats.getAggregateBatteryConsumer(
-                        BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE)
+        assertBatteryConsumer(stats, 360.0, 60 * MINUTE_IN_MS);
+        assertBatteryConsumer(stats, APP_UID, 360.0, 60 * MINUTE_IN_MS);
+        stats.close();
+
+        mStatsRule.waitForBackgroundThread();
+
+        assertThat(count[0]).isEqualTo(expectedNumberOfUpdates);
+    }
+
+    private BatteryUsageStatsProvider createBatteryUsageStatsProvider(
+            int accumulatedBatteryUsageStatsSpanSize) {
+        MultiStatePowerAttributor powerAttributor = new MultiStatePowerAttributor(mContext,
+                mPowerStatsStore, mStatsRule.getPowerProfile(), mStatsRule.getCpuScalingPolicies(),
+                () -> 3500);
+        for (int powerComponentId = 0; powerComponentId < BatteryConsumer.POWER_COMPONENT_COUNT;
+                powerComponentId++) {
+            powerAttributor.setPowerComponentSupported(powerComponentId, true);
+        }
+        powerAttributor.setPowerComponentSupported(BatteryConsumer.POWER_COMPONENT_ANY, true);
+
+        return new BatteryUsageStatsProvider(mContext, powerAttributor,
+                mStatsRule.getPowerProfile(), mStatsRule.getCpuScalingPolicies(), mPowerStatsStore,
+                accumulatedBatteryUsageStatsSpanSize, mMockClock, mMonotonicClock);
+    }
+
+    private static void assertBatteryConsumer(BatteryUsageStats stats, double expectedPowerMah,
+            long expectedDurationMs) {
+        AggregateBatteryConsumer aggregatedConsumer = stats.getAggregateBatteryConsumer(
+                BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE);
+        assertThat(aggregatedConsumer
                 .getConsumedPower(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT))
                 .isWithin(0.0001)
-                .of(360.0);  // 360 mA * 1.0 hour
-        assertThat(stats.getAggregateBatteryConsumer(
-                        BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE)
+                .of(expectedPowerMah);
+        assertThat(aggregatedConsumer
                 .getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT))
-                .isEqualTo(60 * MINUTE_IN_MS);
+                .isEqualTo(expectedDurationMs);
+    }
 
+    private static void assertBatteryConsumer(BatteryUsageStats stats, int uid,
+            double expectedPowerMah, long expectedDurationMs) {
         final UidBatteryConsumer uidBatteryConsumer = stats.getUidBatteryConsumers().stream()
-                .filter(uid -> uid.getUid() == APP_UID).findFirst().get();
+                .filter(u -> u.getUid() == uid).findFirst().get();
         assertThat(uidBatteryConsumer
                 .getConsumedPower(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT))
                 .isWithin(0.1)
-                .of(360.0);
+                .of(expectedPowerMah);
         assertThat(uidBatteryConsumer
                 .getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT))
-                .isEqualTo(60 * MINUTE_IN_MS);
-
-        assertThat(count[0]).isEqualTo(expectedNumberOfUpdates);
-
-        stats.close();
+                .isEqualTo(expectedDurationMs);
     }
 
     private void setTime(long timeMs) {
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsRule.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsRule.java
index a3c7ece..9e7e0b6 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsRule.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsRule.java
@@ -41,6 +41,7 @@
 import android.util.Xml;
 
 import com.android.internal.os.CpuScalingPolicies;
+import com.android.internal.os.MonotonicClock;
 import com.android.internal.os.PowerProfile;
 import com.android.internal.power.EnergyConsumerStats;
 
@@ -65,6 +66,7 @@
 
     private final PowerProfile mPowerProfile;
     private final MockClock mMockClock = new MockClock();
+    private final MonotonicClock mMonotonicClock = new MonotonicClock(666777, mMockClock);
     private String mTestName;
     private boolean mCreateTempDirectory;
     private File mHistoryDir;
@@ -118,7 +120,7 @@
             clearDirectory();
         }
         mBatteryStats = new MockBatteryStatsImpl(mBatteryStatsConfigBuilder.build(),
-                mMockClock, mHistoryDir, mHandler, new PowerStatsUidResolver());
+                mMockClock, mMonotonicClock, mHistoryDir, mHandler, new PowerStatsUidResolver());
         mBatteryStats.setPowerProfile(mPowerProfile);
         mBatteryStats.setCpuScalingPolicies(new CpuScalingPolicies(mCpusByPolicy, mFreqsByPolicy));
         synchronized (mBatteryStats) {
@@ -144,6 +146,10 @@
         return mMockClock;
     }
 
+    public MonotonicClock getMonotonicClock() {
+        return mMonotonicClock;
+    }
+
     public Handler getHandler() {
         return mHandler;
     }
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BluetoothPowerStatsCollectorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BluetoothPowerStatsCollectorTest.java
index e392c5d..3895cb4 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BluetoothPowerStatsCollectorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BluetoothPowerStatsCollectorTest.java
@@ -225,7 +225,10 @@
     }
 
     private PowerStats collectPowerStats() {
-        BluetoothPowerStatsCollector collector = new BluetoothPowerStatsCollector(mInjector);
+        List<BluetoothActivityEnergyInfo> expected = new ArrayList<>();
+        List<BluetoothActivityEnergyInfo> observed = new ArrayList<>();
+        BluetoothPowerStatsCollector collector = new BluetoothPowerStatsCollector(mInjector,
+                (info, elapsedRealtimeMs, uptimeMs) -> observed.add(info));
         collector.setEnabled(true);
 
         when(mConsumedEnergyRetriever.getVoltageMv()).thenReturn(3500);
@@ -236,6 +239,7 @@
                 mockUidTraffic(APP_UID1, 100, 200),
                 mockUidTraffic(APP_UID2, 300, 400),
                 mockUidTraffic(ISOLATED_UID, 500, 600));
+        expected.add(mBluetoothActivityEnergyInfo);
 
         mUidScanTimes.put(APP_UID1, 100);
 
@@ -248,6 +252,7 @@
                 mockUidTraffic(APP_UID1, 1100, 2200),
                 mockUidTraffic(APP_UID2, 3300, 4400),
                 mockUidTraffic(ISOLATED_UID, 5500, 6600));
+        expected.add(mBluetoothActivityEnergyInfo);
 
         mUidScanTimes.clear();
         mUidScanTimes.put(APP_UID1, 200);
@@ -257,7 +262,10 @@
         mockConsumedEnergy(777, 64321);
 
         mStatsRule.setTime(20000, 20000);
-        return collector.collectStats();
+        PowerStats powerStats = collector.collectStats();
+
+        assertThat(observed).isEqualTo(expected);
+        return powerStats;
     }
 
     private void mockConsumedEnergy(int consumerId, long energyUWs) {
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/MockBatteryStatsImpl.java b/services/tests/powerstatstests/src/com/android/server/power/stats/MockBatteryStatsImpl.java
index b374a32..4b6fcc3 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/MockBatteryStatsImpl.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/MockBatteryStatsImpl.java
@@ -77,16 +77,23 @@
                 new PowerStatsUidResolver());
     }
 
-    MockBatteryStatsImpl(BatteryStatsConfig config, Clock clock, File historyDirectory,
-            Handler handler, PowerStatsUidResolver powerStatsUidResolver) {
-        super(config, clock, new MonotonicClock(0, clock), historyDirectory, handler,
+    MockBatteryStatsImpl(BatteryStatsConfig config, Clock clock,
+            File historyDirectory, Handler handler, PowerStatsUidResolver powerStatsUidResolver) {
+        this(config, clock, new MonotonicClock(0, clock), historyDirectory, handler,
+                powerStatsUidResolver);
+    }
+
+    MockBatteryStatsImpl(BatteryStatsConfig config, Clock clock, MonotonicClock monotonicClock,
+            File historyDirectory, Handler handler, PowerStatsUidResolver powerStatsUidResolver) {
+        super(config, clock, monotonicClock, historyDirectory, handler,
                 mock(PlatformIdleStateCallback.class), mock(EnergyStatsRetriever.class),
                 mock(UserInfoProvider.class), mockPowerProfile(),
                 new CpuScalingPolicies(new SparseArray<>(), new SparseArray<>()),
                 powerStatsUidResolver, mock(FrameworkStatsLogger.class),
                 mock(BatteryStatsHistory.TraceDelegate.class),
                 mock(BatteryStatsHistory.EventLogger.class));
-        setMaxHistoryBuffer(128 * 1024);
+        mConstants.MAX_HISTORY_BUFFER = 128 * 1024;
+        mConstants.onChange();
 
         setExternalStatsSyncLocked(mExternalStatsSync);
         informThatAllExternalStatsAreFlushed();
@@ -251,20 +258,6 @@
     }
 
     @GuardedBy("this")
-    public MockBatteryStatsImpl setMaxHistoryFiles(int maxHistoryFiles) {
-        mConstants.MAX_HISTORY_FILES = maxHistoryFiles;
-        mConstants.onChange();
-        return this;
-    }
-
-    @GuardedBy("this")
-    public MockBatteryStatsImpl setMaxHistoryBuffer(int maxHistoryBuffer) {
-        mConstants.MAX_HISTORY_BUFFER = maxHistoryBuffer;
-        mConstants.onChange();
-        return this;
-    }
-
-    @GuardedBy("this")
     public MockBatteryStatsImpl setPerUidModemModel(int perUidModemModel) {
         mConstants.PER_UID_MODEM_MODEL = perUidModemModel;
         mConstants.onChange();
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/BluetoothPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/BluetoothPowerStatsProcessorTest.java
index 2c580e5..6013186 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/BluetoothPowerStatsProcessorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/BluetoothPowerStatsProcessorTest.java
@@ -170,7 +170,7 @@
         PowerComponentAggregatedPowerStats aggregatedStats = createAggregatedPowerStats(
                 () -> new BluetoothPowerStatsProcessor(mStatsRule.getPowerProfile()));
 
-        BluetoothPowerStatsCollector collector = new BluetoothPowerStatsCollector(mInjector);
+        BluetoothPowerStatsCollector collector = new BluetoothPowerStatsCollector(mInjector, null);
         collector.setEnabled(true);
         mBluetoothActivityEnergyInfo = mockBluetoothActivityEnergyInfo(1000, 600, 100, 200,
                 mockUidTraffic(APP_UID1, 100, 200),
@@ -271,7 +271,7 @@
         PowerComponentAggregatedPowerStats aggregatedStats = createAggregatedPowerStats(
                 () -> new BluetoothPowerStatsProcessor(mStatsRule.getPowerProfile()));
 
-        BluetoothPowerStatsCollector collector = new BluetoothPowerStatsCollector(mInjector);
+        BluetoothPowerStatsCollector collector = new BluetoothPowerStatsCollector(mInjector, null);
         collector.setEnabled(true);
         mBluetoothActivityEnergyInfo = mockBluetoothActivityEnergyInfo(1000, 600, 100, 200,
                 mockUidTraffic(APP_UID1, 100, 200),
@@ -371,7 +371,7 @@
         PowerComponentAggregatedPowerStats aggregatedStats = createAggregatedPowerStats(
                 () -> new BluetoothPowerStatsProcessor(mStatsRule.getPowerProfile()));
 
-        BluetoothPowerStatsCollector collector = new BluetoothPowerStatsCollector(mInjector);
+        BluetoothPowerStatsCollector collector = new BluetoothPowerStatsCollector(mInjector, null);
         collector.setEnabled(true);
         mBluetoothActivityEnergyInfo = mockBluetoothActivityEnergyInfo(1000, 600, 100, 200,
                 mockUidTraffic(APP_UID1, 100, 200),
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/PowerStatsAggregatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/PowerStatsAggregatorTest.java
index f312bed..3bdbcb5 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/PowerStatsAggregatorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/PowerStatsAggregatorTest.java
@@ -263,6 +263,34 @@
         });
     }
 
+    @Test
+    public void emptyHistory() {
+        PowerStats.Descriptor descriptor = new PowerStats.Descriptor(TEST_POWER_COMPONENT,
+                "majorDrain", 1, null, 0, 1, new PersistableBundle());
+        PowerStats powerStats = new PowerStats(descriptor);
+
+        mHistory.forceRecordAllHistory();
+
+        advance(1000);
+
+        long firstItemTimestamp = mMonotonicClock.monotonicTime();
+        powerStats.stats = new long[]{24};
+        mHistory.recordPowerStats(mClock.realtime, mClock.uptime, powerStats);
+
+        advance(1000);
+
+        long secondItemTimestamp = mMonotonicClock.monotonicTime();
+        powerStats.stats = new long[]{42};
+        mHistory.recordPowerStats(mClock.realtime, mClock.uptime, powerStats);
+
+        mAggregator.aggregatePowerStats(mHistory, firstItemTimestamp + 1, secondItemTimestamp,
+                stats -> {
+                    mAggregatedStatsCount++;
+                });
+
+        assertThat(mAggregatedStatsCount).isEqualTo(0);
+    }
+
     private void advance(long durationMs) {
         mClock.realtime += durationMs;
         mClock.uptime += durationMs;
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/PowerStatsExporterTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/PowerStatsExporterTest.java
index 38fe613..d243f92 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/PowerStatsExporterTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/PowerStatsExporterTest.java
@@ -408,7 +408,7 @@
         BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder(
                 new String[]{"cu570m"},
                 /* includeProcessStateData */ true, true, true, /* powerThreshold */ 0);
-        exportAggregatedPowerStats(builder, 3700, 6700);
+        exportAggregatedPowerStats(builder, 3700, 7500);
 
         BatteryUsageStats actual = builder.build();
         String message = "Actual BatteryUsageStats: " + actual;
diff --git a/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java b/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java
index 8024915..71a2651 100644
--- a/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java
@@ -16,7 +16,12 @@
 
 package com.android.server;
 
-import static com.android.server.GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS;
+import static android.service.quickaccesswallet.Flags.FLAG_LAUNCH_WALLET_OPTION_ON_POWER_DOUBLE_TAP;
+import static android.service.quickaccesswallet.Flags.launchWalletOptionOnPowerDoubleTap;
+
+import static com.android.server.GestureLauncherService.LAUNCH_CAMERA_ON_DOUBLE_TAP_POWER;
+import static com.android.server.GestureLauncherService.LAUNCH_WALLET_ON_DOUBLE_TAP_POWER;
+import static com.android.server.GestureLauncherService.POWER_DOUBLE_TAP_MAX_TIME_MS;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -24,19 +29,27 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.app.PendingIntent;
 import android.app.StatusBarManager;
+import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.res.Resources;
 import android.os.Looper;
 import android.os.UserHandle;
 import android.platform.test.annotations.Presubmit;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
 import android.provider.Settings;
+import android.service.quickaccesswallet.QuickAccessWalletClient;
 import android.telecom.TelecomManager;
 import android.test.mock.MockContentResolver;
 import android.testing.TestableLooper;
@@ -55,6 +68,7 @@
 
 import org.junit.Before;
 import org.junit.BeforeClass;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
@@ -62,6 +76,8 @@
 import org.mockito.MockitoAnnotations;
 
 import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
 
 /**
  * Unit tests for {@link GestureLauncherService}.
@@ -90,9 +106,32 @@
     private @Mock TelecomManager mTelecomManager;
     private @Mock MetricsLogger mMetricsLogger;
     @Mock private UiEventLogger mUiEventLogger;
+    @Mock private QuickAccessWalletClient mQuickAccessWalletClient;
     private MockContentResolver mContentResolver;
     private GestureLauncherService mGestureLauncherService;
 
+    private Context mInstrumentationContext =
+            InstrumentationRegistry.getInstrumentation().getContext();
+
+    private static final String LAUNCH_TEST_WALLET_ACTION = "LAUNCH_TEST_WALLET_ACTION";
+    private static final String LAUNCH_FALLBACK_ACTION = "LAUNCH_FALLBACK_ACTION";
+    private PendingIntent mGesturePendingIntent =
+            PendingIntent.getBroadcast(
+                    mInstrumentationContext,
+                    0,
+                    new Intent(LAUNCH_TEST_WALLET_ACTION),
+                    PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_ONE_SHOT);
+
+    private PendingIntent mFallbackPendingIntent =
+            PendingIntent.getBroadcast(
+                    mInstrumentationContext,
+                    0,
+                    new Intent(LAUNCH_FALLBACK_ACTION),
+                    PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_ONE_SHOT);
+
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
     @BeforeClass
     public static void oneTimeInitialization() {
         if (Looper.myLooper() == null) {
@@ -115,9 +154,49 @@
         when(mContext.getContentResolver()).thenReturn(mContentResolver);
         when(mContext.getSystemService(Context.TELECOM_SERVICE)).thenReturn(mTelecomManager);
         when(mTelecomManager.createLaunchEmergencyDialerIntent(null)).thenReturn(new Intent());
+        when(mQuickAccessWalletClient.isWalletServiceAvailable()).thenReturn(true);
 
-        mGestureLauncherService = new GestureLauncherService(mContext, mMetricsLogger,
-                mUiEventLogger);
+        mGestureLauncherService =
+                new GestureLauncherService(
+                        mContext, mMetricsLogger, mQuickAccessWalletClient, mUiEventLogger);
+
+        withDoubleTapPowerGestureEnableSettingValue(true);
+        withDefaultDoubleTapPowerGestureAction(LAUNCH_CAMERA_ON_DOUBLE_TAP_POWER);
+    }
+
+    private WalletLaunchedReceiver registerWalletLaunchedReceiver(String action) {
+        IntentFilter filter = new IntentFilter(action);
+        WalletLaunchedReceiver receiver = new WalletLaunchedReceiver();
+        mInstrumentationContext.registerReceiver(receiver, filter, Context.RECEIVER_EXPORTED);
+        return receiver;
+    }
+
+    /**
+     * A simple {@link BroadcastReceiver} implementation that counts down a {@link CountDownLatch}
+     * when a matching message is received
+     */
+    private static final class WalletLaunchedReceiver extends BroadcastReceiver {
+        private static final int TIMEOUT_SECONDS = 3;
+
+        private final CountDownLatch mLatch;
+
+        WalletLaunchedReceiver() {
+            mLatch = new CountDownLatch(1);
+        }
+
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            mLatch.countDown();
+            context.unregisterReceiver(this);
+        }
+
+        Boolean waitUntilShown() {
+            try {
+                return mLatch.await(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+            } catch (InterruptedException e) {
+                return false;
+            }
+        }
     }
 
     @Test
@@ -134,37 +213,123 @@
 
     @Test
     public void testIsCameraDoubleTapPowerSettingEnabled_configFalseSettingDisabled() {
-        withCameraDoubleTapPowerEnableConfigValue(false);
-        withCameraDoubleTapPowerDisableSettingValue(1);
+        if (launchWalletOptionOnPowerDoubleTap()) {
+            withDoubleTapPowerEnabledConfigValue(false);
+            withDoubleTapPowerGestureEnableSettingValue(false);
+            withDefaultDoubleTapPowerGestureAction(LAUNCH_CAMERA_ON_DOUBLE_TAP_POWER);
+        } else {
+            withCameraDoubleTapPowerEnableConfigValue(false);
+            withCameraDoubleTapPowerDisableSettingValue(1);
+        }
         assertFalse(mGestureLauncherService.isCameraDoubleTapPowerSettingEnabled(
                 mContext, FAKE_USER_ID));
     }
 
     @Test
     public void testIsCameraDoubleTapPowerSettingEnabled_configFalseSettingEnabled() {
-        withCameraDoubleTapPowerEnableConfigValue(false);
-        withCameraDoubleTapPowerDisableSettingValue(0);
-        assertFalse(mGestureLauncherService.isCameraDoubleTapPowerSettingEnabled(
-                mContext, FAKE_USER_ID));
+        if (launchWalletOptionOnPowerDoubleTap()) {
+            withDoubleTapPowerEnabledConfigValue(false);
+            withDoubleTapPowerGestureEnableSettingValue(true);
+            withDefaultDoubleTapPowerGestureAction(LAUNCH_CAMERA_ON_DOUBLE_TAP_POWER);
+            assertTrue(mGestureLauncherService.isCameraDoubleTapPowerSettingEnabled(
+                    mContext, FAKE_USER_ID));
+        } else {
+            withCameraDoubleTapPowerEnableConfigValue(false);
+            withCameraDoubleTapPowerDisableSettingValue(0);
+            assertFalse(mGestureLauncherService.isCameraDoubleTapPowerSettingEnabled(
+                    mContext, FAKE_USER_ID));
+        }
     }
 
     @Test
     public void testIsCameraDoubleTapPowerSettingEnabled_configTrueSettingDisabled() {
-        withCameraDoubleTapPowerEnableConfigValue(true);
-        withCameraDoubleTapPowerDisableSettingValue(1);
+        if (launchWalletOptionOnPowerDoubleTap()) {
+            withDoubleTapPowerEnabledConfigValue(true);
+            withDoubleTapPowerGestureEnableSettingValue(false);
+            withDefaultDoubleTapPowerGestureAction(LAUNCH_CAMERA_ON_DOUBLE_TAP_POWER);
+        } else {
+            withCameraDoubleTapPowerEnableConfigValue(true);
+            withCameraDoubleTapPowerDisableSettingValue(1);
+        }
         assertFalse(mGestureLauncherService.isCameraDoubleTapPowerSettingEnabled(
                 mContext, FAKE_USER_ID));
     }
 
     @Test
     public void testIsCameraDoubleTapPowerSettingEnabled_configTrueSettingEnabled() {
-        withCameraDoubleTapPowerEnableConfigValue(true);
-        withCameraDoubleTapPowerDisableSettingValue(0);
+        if (launchWalletOptionOnPowerDoubleTap()) {
+            withDoubleTapPowerEnabledConfigValue(true);
+            withDoubleTapPowerGestureEnableSettingValue(true);
+            withDefaultDoubleTapPowerGestureAction(LAUNCH_CAMERA_ON_DOUBLE_TAP_POWER);
+        } else {
+            withCameraDoubleTapPowerEnableConfigValue(true);
+            withCameraDoubleTapPowerDisableSettingValue(0);
+        }
         assertTrue(mGestureLauncherService.isCameraDoubleTapPowerSettingEnabled(
                 mContext, FAKE_USER_ID));
     }
 
     @Test
+    @RequiresFlagsEnabled(FLAG_LAUNCH_WALLET_OPTION_ON_POWER_DOUBLE_TAP)
+    public void testIsCameraDoubleTapPowerSettingEnabled_actionWallet() {
+        withDoubleTapPowerEnabledConfigValue(true);
+        withDoubleTapPowerGestureEnableSettingValue(true);
+        withDefaultDoubleTapPowerGestureAction(LAUNCH_WALLET_ON_DOUBLE_TAP_POWER);
+
+        assertFalse(
+                mGestureLauncherService.isCameraDoubleTapPowerSettingEnabled(
+                        mContext, FAKE_USER_ID));
+    }
+
+    @Test
+    @RequiresFlagsEnabled(FLAG_LAUNCH_WALLET_OPTION_ON_POWER_DOUBLE_TAP)
+    public void testIsWalletDoubleTapPowerSettingEnabled() {
+        withDoubleTapPowerEnabledConfigValue(true);
+        withDoubleTapPowerGestureEnableSettingValue(true);
+        withDefaultDoubleTapPowerGestureAction(LAUNCH_WALLET_ON_DOUBLE_TAP_POWER);
+
+        assertTrue(
+                mGestureLauncherService.isWalletDoubleTapPowerSettingEnabled(
+                        mContext, FAKE_USER_ID));
+    }
+
+    @Test
+    @RequiresFlagsEnabled(FLAG_LAUNCH_WALLET_OPTION_ON_POWER_DOUBLE_TAP)
+    public void testIsWalletDoubleTapPowerSettingEnabled_configDisabled() {
+        withDoubleTapPowerEnabledConfigValue(false);
+        withDoubleTapPowerGestureEnableSettingValue(true);
+        withDefaultDoubleTapPowerGestureAction(LAUNCH_WALLET_ON_DOUBLE_TAP_POWER);
+
+        assertTrue(
+                mGestureLauncherService.isWalletDoubleTapPowerSettingEnabled(
+                        mContext, FAKE_USER_ID));
+    }
+
+    @Test
+    @RequiresFlagsEnabled(FLAG_LAUNCH_WALLET_OPTION_ON_POWER_DOUBLE_TAP)
+    public void testIsWalletDoubleTapPowerSettingEnabled_settingDisabled() {
+        withDoubleTapPowerEnabledConfigValue(true);
+        withDoubleTapPowerGestureEnableSettingValue(false);
+        withDefaultDoubleTapPowerGestureAction(LAUNCH_WALLET_ON_DOUBLE_TAP_POWER);
+
+        assertFalse(
+                mGestureLauncherService.isWalletDoubleTapPowerSettingEnabled(
+                        mContext, FAKE_USER_ID));
+    }
+
+    @Test
+    @RequiresFlagsEnabled(FLAG_LAUNCH_WALLET_OPTION_ON_POWER_DOUBLE_TAP)
+    public void testIsWalletDoubleTapPowerSettingEnabled_actionCamera() {
+        withDoubleTapPowerEnabledConfigValue(true);
+        withDoubleTapPowerGestureEnableSettingValue(true);
+        withDefaultDoubleTapPowerGestureAction(LAUNCH_CAMERA_ON_DOUBLE_TAP_POWER);
+
+        assertFalse(
+                mGestureLauncherService.isWalletDoubleTapPowerSettingEnabled(
+                        mContext, FAKE_USER_ID));
+    }
+
+    @Test
     public void testIsEmergencyGestureSettingEnabled_settingDisabled() {
         withEmergencyGestureEnabledConfigValue(true);
         withEmergencyGestureEnabledSettingValue(false);
@@ -245,12 +410,9 @@
 
     @Test
     public void testInterceptPowerKeyDown_firstPowerDownCameraPowerGestureOnInteractive() {
-        withCameraDoubleTapPowerEnableConfigValue(true);
-        withCameraDoubleTapPowerDisableSettingValue(0);
-        mGestureLauncherService.updateCameraDoubleTapPowerEnabled();
+        enableCameraGesture();
 
-        long eventTime = INITIAL_EVENT_TIME_MILLIS +
-                CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
+        long eventTime = INITIAL_EVENT_TIME_MILLIS + POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
         KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
                 IGNORED_REPEAT);
         boolean interactive = true;
@@ -284,8 +446,12 @@
 
     @Test
     public void testInterceptPowerKeyDown_intervalInBoundsCameraPowerGestureOffInteractive() {
-        withCameraDoubleTapPowerEnableConfigValue(false);
-        withCameraDoubleTapPowerDisableSettingValue(1);
+        if (launchWalletOptionOnPowerDoubleTap()) {
+            withDoubleTapPowerGestureEnableSettingValue(false);
+        } else {
+            withCameraDoubleTapPowerEnableConfigValue(false);
+            withCameraDoubleTapPowerDisableSettingValue(1);
+        }
         mGestureLauncherService.updateCameraDoubleTapPowerEnabled();
 
         long eventTime = INITIAL_EVENT_TIME_MILLIS;
@@ -298,7 +464,7 @@
         assertFalse(intercepted);
         assertFalse(outLaunched.value);
 
-        final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
+        final long interval = POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
         eventTime += interval;
         keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
                 IGNORED_REPEAT);
@@ -309,7 +475,7 @@
         assertFalse(outLaunched.value);
 
         verify(mMetricsLogger, never())
-            .action(eq(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE), anyInt());
+                .action(eq(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE), anyInt());
         verify(mUiEventLogger, never()).log(any());
 
         final ArgumentCaptor<Integer> intervalCaptor = ArgumentCaptor.forClass(Integer.class);
@@ -329,8 +495,12 @@
 
     @Test
     public void testInterceptPowerKeyDown_intervalMidBoundsCameraPowerGestureOffInteractive() {
-        withCameraDoubleTapPowerEnableConfigValue(false);
-        withCameraDoubleTapPowerDisableSettingValue(1);
+        if (launchWalletOptionOnPowerDoubleTap()) {
+            withDoubleTapPowerGestureEnableSettingValue(false);
+        } else {
+            withCameraDoubleTapPowerEnableConfigValue(false);
+            withCameraDoubleTapPowerDisableSettingValue(1);
+        }
         mGestureLauncherService.updateCameraDoubleTapPowerEnabled();
 
         long eventTime = INITIAL_EVENT_TIME_MILLIS;
@@ -343,7 +513,7 @@
         assertFalse(intercepted);
         assertFalse(outLaunched.value);
 
-        final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS;
+        final long interval = POWER_DOUBLE_TAP_MAX_TIME_MS;
         eventTime += interval;
         keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
                 IGNORED_REPEAT);
@@ -354,7 +524,7 @@
         assertFalse(outLaunched.value);
 
         verify(mMetricsLogger, never())
-            .action(eq(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE), anyInt());
+                .action(eq(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE), anyInt());
         verify(mUiEventLogger, never()).log(any());
 
         final ArgumentCaptor<Integer> intervalCaptor = ArgumentCaptor.forClass(Integer.class);
@@ -401,7 +571,7 @@
         assertFalse(outLaunched.value);
 
         verify(mMetricsLogger, never())
-            .action(eq(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE), anyInt());
+                .action(eq(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE), anyInt());
         verify(mUiEventLogger, never()).log(any());
 
         final ArgumentCaptor<Integer> intervalCaptor = ArgumentCaptor.forClass(Integer.class);
@@ -422,10 +592,7 @@
     @Test
     public void
     testInterceptPowerKeyDown_intervalInBoundsCameraPowerGestureOnInteractiveSetupComplete() {
-        withCameraDoubleTapPowerEnableConfigValue(true);
-        withCameraDoubleTapPowerDisableSettingValue(0);
-        mGestureLauncherService.updateCameraDoubleTapPowerEnabled();
-        withUserSetupCompleteValue(true);
+        enableCameraGesture();
 
         long eventTime = INITIAL_EVENT_TIME_MILLIS;
         KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
@@ -437,7 +604,7 @@
         assertFalse(intercepted);
         assertFalse(outLaunched.value);
 
-        final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
+        final long interval = POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
         eventTime += interval;
         keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
                 IGNORED_REPEAT);
@@ -450,7 +617,7 @@
         verify(mStatusBarManagerInternal).onCameraLaunchGestureDetected(
                 StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP);
         verify(mMetricsLogger)
-            .action(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE, (int) interval);
+                .action(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE, (int) interval);
         verify(mUiEventLogger, times(1))
                 .log(GestureLauncherService.GestureLauncherEvent.GESTURE_CAMERA_DOUBLE_TAP_POWER);
 
@@ -470,15 +637,145 @@
     }
 
     @Test
+    @RequiresFlagsEnabled(FLAG_LAUNCH_WALLET_OPTION_ON_POWER_DOUBLE_TAP)
+    public void
+            testInterceptPowerKeyDown_fiveInboundPresses_walletAndEmergencyEnabled_bothLaunch() {
+        WalletLaunchedReceiver receiver = registerWalletLaunchedReceiver(LAUNCH_TEST_WALLET_ACTION);
+        setUpGetGestureTargetActivityPendingIntent(mGesturePendingIntent);
+        enableEmergencyGesture();
+        enableWalletGesture();
+
+        // First event
+        long eventTime = INITIAL_EVENT_TIME_MILLIS;
+        sendPowerKeyDownToGestureLauncherServiceAndAssertValues(eventTime, false, false);
+
+        final long interval = POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
+        eventTime += interval;
+        sendPowerKeyDownToGestureLauncherServiceAndAssertValues(eventTime, true, true);
+
+        assertTrue(receiver.waitUntilShown());
+
+        // Presses 3 and 4 should not trigger any gesture
+        for (int i = 0; i < 2; i++) {
+            eventTime += interval;
+            sendPowerKeyDownToGestureLauncherServiceAndAssertValues(eventTime, true, false);
+        }
+
+        // Fifth button press should trigger the emergency flow
+        eventTime += interval;
+        sendPowerKeyDownToGestureLauncherServiceAndAssertValues(eventTime, true, true);
+
+        verify(mUiEventLogger, times(1))
+                .log(GestureLauncherService.GestureLauncherEvent.GESTURE_EMERGENCY_TAP_POWER);
+        verify(mStatusBarManagerInternal).onEmergencyActionLaunchGestureDetected();
+    }
+
+    @Test
+    @RequiresFlagsEnabled(FLAG_LAUNCH_WALLET_OPTION_ON_POWER_DOUBLE_TAP)
+    public void testInterceptPowerKeyDown_intervalInBoundsWalletPowerGesture() {
+        WalletLaunchedReceiver receiver = registerWalletLaunchedReceiver(LAUNCH_TEST_WALLET_ACTION);
+        setUpGetGestureTargetActivityPendingIntent(mGesturePendingIntent);
+        enableWalletGesture();
+        enableEmergencyGesture();
+
+        long eventTime = INITIAL_EVENT_TIME_MILLIS;
+        sendPowerKeyDownToGestureLauncherServiceAndAssertValues(eventTime, false, false);
+        final long interval = POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
+        eventTime += interval;
+        sendPowerKeyDownToGestureLauncherServiceAndAssertValues(eventTime, true, true);
+        assertTrue(receiver.waitUntilShown());
+    }
+
+    @Test
+    @RequiresFlagsEnabled(FLAG_LAUNCH_WALLET_OPTION_ON_POWER_DOUBLE_TAP)
+    public void testInterceptPowerKeyDown_walletGestureOn_quickAccessWalletServiceUnavailable() {
+        when(mQuickAccessWalletClient.isWalletServiceAvailable()).thenReturn(false);
+        WalletLaunchedReceiver receiver = registerWalletLaunchedReceiver(LAUNCH_TEST_WALLET_ACTION);
+        setUpGetGestureTargetActivityPendingIntent(mGesturePendingIntent);
+        enableWalletGesture();
+
+        // First event
+        long eventTime = INITIAL_EVENT_TIME_MILLIS;
+        sendPowerKeyDownToGestureLauncherServiceAndAssertValues(eventTime, false, false);
+
+        final long interval = POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
+        eventTime += interval;
+        sendPowerKeyDownToGestureLauncherServiceAndAssertValues(eventTime, true, false);
+
+        assertFalse(receiver.waitUntilShown());
+    }
+
+    @Test
+    public void testInterceptPowerKeyDown_walletGestureOn_userSetupIncomplete() {
+        WalletLaunchedReceiver receiver = registerWalletLaunchedReceiver(LAUNCH_TEST_WALLET_ACTION);
+        setUpGetGestureTargetActivityPendingIntent(mGesturePendingIntent);
+        enableWalletGesture();
+        withUserSetupCompleteValue(false);
+
+        // First event
+        long eventTime = INITIAL_EVENT_TIME_MILLIS;
+        sendPowerKeyDownToGestureLauncherServiceAndAssertValues(eventTime, false, false);
+
+        final long interval = POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
+        eventTime += interval;
+        sendPowerKeyDownToGestureLauncherServiceAndAssertValues(eventTime, false, false);
+
+        assertFalse(receiver.waitUntilShown());
+    }
+
+    @Test
+    @RequiresFlagsEnabled(FLAG_LAUNCH_WALLET_OPTION_ON_POWER_DOUBLE_TAP)
+    public void testInterceptPowerKeyDown_walletPowerGesture_nullPendingIntent() {
+        WalletLaunchedReceiver gestureReceiver =
+                registerWalletLaunchedReceiver(LAUNCH_TEST_WALLET_ACTION);
+        setUpGetGestureTargetActivityPendingIntent(null);
+        WalletLaunchedReceiver fallbackReceiver =
+                registerWalletLaunchedReceiver(LAUNCH_FALLBACK_ACTION);
+        setUpWalletFallbackPendingIntent(mFallbackPendingIntent);
+        enableWalletGesture();
+        enableEmergencyGesture();
+
+        // First event
+        long eventTime = INITIAL_EVENT_TIME_MILLIS;
+        sendPowerKeyDownToGestureLauncherServiceAndAssertValues(eventTime, false, false);
+
+        final long interval = POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
+        eventTime += interval;
+        sendPowerKeyDownToGestureLauncherServiceAndAssertValues(eventTime, true, true);
+
+        assertFalse(gestureReceiver.waitUntilShown());
+        assertTrue(fallbackReceiver.waitUntilShown());
+    }
+
+    @Test
+    @RequiresFlagsEnabled(FLAG_LAUNCH_WALLET_OPTION_ON_POWER_DOUBLE_TAP)
+    public void testInterceptPowerKeyDown_walletPowerGesture_intervalOutOfBounds() {
+        WalletLaunchedReceiver gestureReceiver =
+                registerWalletLaunchedReceiver(LAUNCH_TEST_WALLET_ACTION);
+        setUpGetGestureTargetActivityPendingIntent(null);
+        WalletLaunchedReceiver fallbackReceiver =
+                registerWalletLaunchedReceiver(LAUNCH_FALLBACK_ACTION);
+        setUpWalletFallbackPendingIntent(mFallbackPendingIntent);
+        enableWalletGesture();
+        enableEmergencyGesture();
+
+        // First event
+        long eventTime = INITIAL_EVENT_TIME_MILLIS;
+        sendPowerKeyDownToGestureLauncherServiceAndAssertValues(eventTime, false, false);
+
+        final long interval = POWER_DOUBLE_TAP_MAX_TIME_MS;
+        eventTime += interval;
+        sendPowerKeyDownToGestureLauncherServiceAndAssertValues(eventTime, false, false);
+
+        assertFalse(gestureReceiver.waitUntilShown());
+        assertFalse(fallbackReceiver.waitUntilShown());
+    }
+
+    @Test
     public void
             testInterceptPowerKeyDown_fiveInboundPresses_cameraAndEmergencyEnabled_bothLaunch() {
-        withCameraDoubleTapPowerEnableConfigValue(true);
-        withCameraDoubleTapPowerDisableSettingValue(0);
-        withEmergencyGestureEnabledConfigValue(true);
-        withEmergencyGestureEnabledSettingValue(true);
-        mGestureLauncherService.updateCameraDoubleTapPowerEnabled();
-        mGestureLauncherService.updateEmergencyGestureEnabled();
-        withUserSetupCompleteValue(true);
+        enableCameraGesture();
+        enableEmergencyGesture();
 
         // First button press does nothing
         long eventTime = INITIAL_EVENT_TIME_MILLIS;
@@ -491,7 +788,7 @@
         assertFalse(intercepted);
         assertFalse(outLaunched.value);
 
-        final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
+        final long interval = POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
 
         // 2nd button triggers camera
         eventTime += interval;
@@ -507,7 +804,7 @@
         verify(mStatusBarManagerInternal).onCameraLaunchGestureDetected(
                 StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP);
         verify(mMetricsLogger)
-            .action(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE, (int) interval);
+                .action(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE, (int) interval);
         verify(mUiEventLogger, times(1))
                 .log(GestureLauncherService.GestureLauncherEvent.GESTURE_CAMERA_DOUBLE_TAP_POWER);
 
@@ -580,7 +877,7 @@
         assertFalse(intercepted);
         assertFalse(outLaunched.value);
 
-        final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
+        final long interval = POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
         // 3 more button presses which should not trigger any gesture (camera gesture disabled)
         for (int i = 0; i < 3; i++) {
             eventTime += interval;
@@ -634,7 +931,7 @@
         assertFalse(intercepted);
         assertFalse(outLaunched.value);
 
-        final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
+        final long interval = POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
         // 3 more button presses which should not trigger any gesture, but intercepts action.
         for (int i = 0; i < 3; i++) {
             eventTime += interval;
@@ -737,7 +1034,7 @@
                     interactive, outLaunched);
             assertTrue(intercepted);
             assertFalse(outLaunched.value);
-            interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
+            interval = POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
             eventTime += interval;
         }
     }
@@ -765,7 +1062,7 @@
                     interactive, outLaunched);
             assertTrue(intercepted);
             assertFalse(outLaunched.value);
-            interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
+            interval = POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
             eventTime += interval;
         }
     }
@@ -916,7 +1213,7 @@
         assertFalse(intercepted);
         assertFalse(outLaunched.value);
 
-        final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
+        final long interval = POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
         eventTime += interval;
         keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
                 IGNORED_REPEAT, IGNORED_META_STATE, IGNORED_DEVICE_ID, IGNORED_SCANCODE,
@@ -947,9 +1244,7 @@
     @Test
     public void
     testInterceptPowerKeyDown_intervalInBoundsCameraPowerGestureOnInteractiveSetupIncomplete() {
-        withCameraDoubleTapPowerEnableConfigValue(true);
-        withCameraDoubleTapPowerDisableSettingValue(0);
-        mGestureLauncherService.updateCameraDoubleTapPowerEnabled();
+        enableCameraGesture();
         withUserSetupCompleteValue(false);
 
         long eventTime = INITIAL_EVENT_TIME_MILLIS;
@@ -962,7 +1257,7 @@
         assertFalse(intercepted);
         assertFalse(outLaunched.value);
 
-        final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
+        final long interval = POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
         eventTime += interval;
         keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
                 IGNORED_REPEAT);
@@ -973,7 +1268,7 @@
         assertFalse(outLaunched.value);
 
         verify(mMetricsLogger, never())
-            .action(eq(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE), anyInt());
+                .action(eq(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE), anyInt());
         verify(mUiEventLogger, never()).log(any());
 
         final ArgumentCaptor<Integer> intervalCaptor = ArgumentCaptor.forClass(Integer.class);
@@ -995,9 +1290,7 @@
 
     @Test
     public void testInterceptPowerKeyDown_intervalMidBoundsCameraPowerGestureOnInteractive() {
-        withCameraDoubleTapPowerEnableConfigValue(true);
-        withCameraDoubleTapPowerDisableSettingValue(0);
-        mGestureLauncherService.updateCameraDoubleTapPowerEnabled();
+        enableCameraGesture();
 
         long eventTime = INITIAL_EVENT_TIME_MILLIS;
         KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
@@ -1009,7 +1302,7 @@
         assertFalse(intercepted);
         assertFalse(outLaunched.value);
 
-        final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS;
+        final long interval = POWER_DOUBLE_TAP_MAX_TIME_MS;
         eventTime += interval;
         keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
                 IGNORED_REPEAT);
@@ -1020,7 +1313,7 @@
         assertFalse(outLaunched.value);
 
         verify(mMetricsLogger, never())
-            .action(eq(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE), anyInt());
+                .action(eq(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE), anyInt());
         verify(mUiEventLogger, never()).log(any());
 
         final ArgumentCaptor<Integer> intervalCaptor = ArgumentCaptor.forClass(Integer.class);
@@ -1042,9 +1335,7 @@
 
     @Test
     public void testInterceptPowerKeyDown_intervalOutOfBoundsCameraPowerGestureOnInteractive() {
-        withCameraDoubleTapPowerEnableConfigValue(true);
-        withCameraDoubleTapPowerDisableSettingValue(0);
-        mGestureLauncherService.updateCameraDoubleTapPowerEnabled();
+        enableCameraGesture();
 
         long eventTime = INITIAL_EVENT_TIME_MILLIS;
         KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
@@ -1067,7 +1358,7 @@
         assertFalse(outLaunched.value);
 
         verify(mMetricsLogger, never())
-            .action(eq(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE), anyInt());
+                .action(eq(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE), anyInt());
         verify(mUiEventLogger, never()).log(any());
 
         final ArgumentCaptor<Integer> intervalCaptor = ArgumentCaptor.forClass(Integer.class);
@@ -1087,8 +1378,12 @@
 
     @Test
     public void testInterceptPowerKeyDown_intervalInBoundsCameraPowerGestureOffNotInteractive() {
-        withCameraDoubleTapPowerEnableConfigValue(false);
-        withCameraDoubleTapPowerDisableSettingValue(1);
+        if (launchWalletOptionOnPowerDoubleTap()) {
+            withDoubleTapPowerGestureEnableSettingValue(false);
+        } else {
+            withCameraDoubleTapPowerEnableConfigValue(false);
+            withCameraDoubleTapPowerDisableSettingValue(1);
+        }
         mGestureLauncherService.updateCameraDoubleTapPowerEnabled();
 
         long eventTime = INITIAL_EVENT_TIME_MILLIS;
@@ -1101,7 +1396,7 @@
         assertFalse(intercepted);
         assertFalse(outLaunched.value);
 
-        final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
+        final long interval = POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
         eventTime += interval;
         keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
                 IGNORED_REPEAT);
@@ -1112,7 +1407,7 @@
         assertFalse(outLaunched.value);
 
         verify(mMetricsLogger, never())
-            .action(eq(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE), anyInt());
+                .action(eq(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE), anyInt());
         verify(mUiEventLogger, never()).log(any());
 
         final ArgumentCaptor<Integer> intervalCaptor = ArgumentCaptor.forClass(Integer.class);
@@ -1146,7 +1441,7 @@
         assertFalse(intercepted);
         assertFalse(outLaunched.value);
 
-        final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS;
+        final long interval = POWER_DOUBLE_TAP_MAX_TIME_MS;
         eventTime += interval;
         keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
                 IGNORED_REPEAT);
@@ -1156,7 +1451,7 @@
         assertFalse(intercepted);
         assertFalse(outLaunched.value);
         verify(mMetricsLogger, never())
-            .action(eq(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE), anyInt());
+                .action(eq(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE), anyInt());
         verify(mUiEventLogger, never()).log(any());
 
         final ArgumentCaptor<Integer> intervalCaptor = ArgumentCaptor.forClass(Integer.class);
@@ -1202,7 +1497,7 @@
         assertFalse(intercepted);
         assertFalse(outLaunched.value);
         verify(mMetricsLogger, never())
-            .action(eq(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE), anyInt());
+                .action(eq(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE), anyInt());
         verify(mUiEventLogger, never()).log(any());
 
         final ArgumentCaptor<Integer> intervalCaptor = ArgumentCaptor.forClass(Integer.class);
@@ -1223,10 +1518,7 @@
     @Test
     public void
     testInterceptPowerKeyDown_intervalInBoundsCameraPowerGestureOnNotInteractiveSetupComplete() {
-        withCameraDoubleTapPowerEnableConfigValue(true);
-        withCameraDoubleTapPowerDisableSettingValue(0);
-        mGestureLauncherService.updateCameraDoubleTapPowerEnabled();
-        withUserSetupCompleteValue(true);
+        enableCameraGesture();
 
         long eventTime = INITIAL_EVENT_TIME_MILLIS;
         KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
@@ -1238,7 +1530,7 @@
         assertFalse(intercepted);
         assertFalse(outLaunched.value);
 
-        final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
+        final long interval = POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
         eventTime += interval;
         keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
                 IGNORED_REPEAT);
@@ -1250,7 +1542,7 @@
         verify(mStatusBarManagerInternal).onCameraLaunchGestureDetected(
                 StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP);
         verify(mMetricsLogger)
-            .action(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE, (int) interval);
+                .action(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE, (int) interval);
         verify(mUiEventLogger, times(1))
                 .log(GestureLauncherService.GestureLauncherEvent.GESTURE_CAMERA_DOUBLE_TAP_POWER);
 
@@ -1272,9 +1564,7 @@
     @Test
     public void
     testInterceptPowerKeyDown_intervalInBoundsCameraPowerGestureOnNotInteractiveSetupIncomplete() {
-        withCameraDoubleTapPowerEnableConfigValue(true);
-        withCameraDoubleTapPowerDisableSettingValue(0);
-        mGestureLauncherService.updateCameraDoubleTapPowerEnabled();
+        enableCameraGesture();
         withUserSetupCompleteValue(false);
 
         long eventTime = INITIAL_EVENT_TIME_MILLIS;
@@ -1287,7 +1577,7 @@
         assertFalse(intercepted);
         assertFalse(outLaunched.value);
 
-        final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
+        final long interval = POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
         eventTime += interval;
         keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
                 IGNORED_REPEAT);
@@ -1298,7 +1588,7 @@
         assertFalse(outLaunched.value);
 
         verify(mMetricsLogger, never())
-            .action(eq(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE), anyInt());
+                .action(eq(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE), anyInt());
         verify(mUiEventLogger, never()).log(any());
 
         final ArgumentCaptor<Integer> intervalCaptor = ArgumentCaptor.forClass(Integer.class);
@@ -1332,7 +1622,7 @@
         assertFalse(intercepted);
         assertFalse(outLaunched.value);
 
-        final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS;
+        final long interval = POWER_DOUBLE_TAP_MAX_TIME_MS;
         eventTime += interval;
         keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
                 IGNORED_REPEAT);
@@ -1343,7 +1633,7 @@
         assertFalse(outLaunched.value);
 
         verify(mMetricsLogger, never())
-            .action(eq(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE), anyInt());
+                .action(eq(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE), anyInt());
         verify(mUiEventLogger, never()).log(any());
 
         final ArgumentCaptor<Integer> intervalCaptor = ArgumentCaptor.forClass(Integer.class);
@@ -1365,9 +1655,7 @@
 
     @Test
     public void testInterceptPowerKeyDown_intervalOutOfBoundsCameraPowerGestureOnNotInteractive() {
-        withCameraDoubleTapPowerEnableConfigValue(true);
-        withCameraDoubleTapPowerDisableSettingValue(0);
-        mGestureLauncherService.updateCameraDoubleTapPowerEnabled();
+        enableCameraGesture();
 
         long eventTime = INITIAL_EVENT_TIME_MILLIS;
         KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
@@ -1390,7 +1678,7 @@
         assertFalse(outLaunched.value);
 
         verify(mMetricsLogger, never())
-            .action(eq(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE), anyInt());
+                .action(eq(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE), anyInt());
         verify(mUiEventLogger, never()).log(any());
 
         final ArgumentCaptor<Integer> intervalCaptor = ArgumentCaptor.forClass(Integer.class);
@@ -1414,7 +1702,7 @@
      * @return last event time.
      */
     private long triggerEmergencyGesture() {
-        return triggerEmergencyGesture(CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1);
+        return triggerEmergencyGesture(POWER_DOUBLE_TAP_MAX_TIME_MS - 1);
     }
 
     /**
@@ -1473,6 +1761,27 @@
                 .thenReturn(enableConfigValue);
     }
 
+    private void withDoubleTapPowerEnabledConfigValue(boolean enable) {
+        when(mResources.getBoolean(com.android.internal.R.bool.config_doubleTapPowerGestureEnabled))
+                .thenReturn(enable);
+    }
+
+    private void withDoubleTapPowerGestureEnableSettingValue(boolean enable) {
+        Settings.Secure.putIntForUser(
+                mContentResolver,
+                Settings.Secure.DOUBLE_TAP_POWER_BUTTON_GESTURE_ENABLED,
+                enable ? 1 : 0,
+                UserHandle.USER_CURRENT);
+    }
+
+    private void withDefaultDoubleTapPowerGestureAction(int action) {
+        Settings.Secure.putIntForUser(
+                mContentResolver,
+                Settings.Secure.DOUBLE_TAP_POWER_BUTTON_GESTURE,
+                action,
+                UserHandle.USER_CURRENT);
+    }
+
     private void withEmergencyGestureEnabledConfigValue(boolean enableConfigValue) {
         when(mResources.getBoolean(
                 com.android.internal.R.bool.config_emergencyGestureEnabled))
@@ -1510,4 +1819,72 @@
                 userSetupCompleteValue,
                 UserHandle.USER_CURRENT);
     }
+
+    private void setUpGetGestureTargetActivityPendingIntent(PendingIntent pendingIntent) {
+        doAnswer(
+                invocation -> {
+                    QuickAccessWalletClient.GesturePendingIntentCallback callback =
+                            (QuickAccessWalletClient.GesturePendingIntentCallback)
+                                    invocation.getArguments()[1];
+                    callback.onGesturePendingIntentRetrieved(pendingIntent);
+                    return null;
+                })
+                .when(mQuickAccessWalletClient)
+                .getGestureTargetActivityPendingIntent(any(), any());
+    }
+
+    private void setUpWalletFallbackPendingIntent(PendingIntent pendingIntent) {
+        doAnswer(
+                invocation -> {
+                    QuickAccessWalletClient.WalletPendingIntentCallback callback =
+                            (QuickAccessWalletClient.WalletPendingIntentCallback)
+                                    invocation.getArguments()[1];
+                    callback.onWalletPendingIntentRetrieved(pendingIntent);
+                    return null;
+                })
+                .when(mQuickAccessWalletClient)
+                .getWalletPendingIntent(any(), any());
+    }
+
+    private void enableWalletGesture() {
+        withDefaultDoubleTapPowerGestureAction(LAUNCH_WALLET_ON_DOUBLE_TAP_POWER);
+        withDoubleTapPowerGestureEnableSettingValue(true);
+        withDoubleTapPowerEnabledConfigValue(true);
+
+        mGestureLauncherService.updateWalletDoubleTapPowerEnabled();
+        withUserSetupCompleteValue(true);
+    }
+
+    private void enableEmergencyGesture() {
+        withEmergencyGestureEnabledConfigValue(true);
+        withEmergencyGestureEnabledSettingValue(true);
+        mGestureLauncherService.updateEmergencyGestureEnabled();
+        withUserSetupCompleteValue(true);
+    }
+
+    private void enableCameraGesture() {
+        if (launchWalletOptionOnPowerDoubleTap()) {
+            withDoubleTapPowerEnabledConfigValue(true);
+            withDoubleTapPowerGestureEnableSettingValue(true);
+            withDefaultDoubleTapPowerGestureAction(LAUNCH_CAMERA_ON_DOUBLE_TAP_POWER);
+        } else {
+            withCameraDoubleTapPowerEnableConfigValue(true);
+            withCameraDoubleTapPowerDisableSettingValue(0);
+        }
+        mGestureLauncherService.updateCameraDoubleTapPowerEnabled();
+        withUserSetupCompleteValue(true);
+    }
+
+    private void sendPowerKeyDownToGestureLauncherServiceAndAssertValues(
+            long eventTime, boolean expectedIntercept, boolean expectedOutLaunchedValue) {
+        KeyEvent keyEvent =
+                new KeyEvent(
+                        IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE, IGNORED_REPEAT);
+        boolean interactive = true;
+        MutableBoolean outLaunched = new MutableBoolean(true);
+        boolean intercepted =
+                mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive, outLaunched);
+        assertEquals(intercepted, expectedIntercept);
+        assertEquals(outLaunched.value, expectedOutLaunchedValue);
+    }
 }
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 a2965b3..fa78dfc 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
@@ -64,6 +64,7 @@
 import android.accessibilityservice.AccessibilityServiceInfo;
 import android.accessibilityservice.IAccessibilityServiceClient;
 import android.annotation.NonNull;
+import android.annotation.UserIdInt;
 import android.app.PendingIntent;
 import android.app.RemoteAction;
 import android.app.admin.DevicePolicyManager;
@@ -1652,10 +1653,17 @@
 
     @Test
     @EnableFlags(android.view.accessibility.Flags.FLAG_A11Y_QS_SHORTCUT)
-    public void restoreShortcutTargets_qs_a11yQsTargetsRestored() {
-        // TODO: remove the assumption when we fix b/381294327
+    @DisableFlags(android.view.accessibility.Flags.FLAG_RESTORE_A11Y_SECURE_SETTINGS_ON_HSUM_DEVICE)
+    public void restoreShortcutTargetsAssumeUser0_qs_a11yQsTargetsRestored() {
         assumeTrue("The test is setup to run as a user 0",
                 mTestableContext.getUserId() == UserHandle.USER_SYSTEM);
+        restoreShortcutTargets_qs_a11yQsTargetsRestored();
+    }
+
+    @Test
+    @EnableFlags({android.view.accessibility.Flags.FLAG_A11Y_QS_SHORTCUT,
+            android.view.accessibility.Flags.FLAG_RESTORE_A11Y_SECURE_SETTINGS_ON_HSUM_DEVICE})
+    public void restoreShortcutTargets_qs_a11yQsTargetsRestored() {
         String daltonizerTile =
                 AccessibilityShortcutController.DALTONIZER_COMPONENT_NAME.flattenToString();
         String colorInversionTile =
@@ -1667,7 +1675,7 @@
 
         broadcastSettingRestored(
                 ShortcutUtils.convertToKey(QUICK_SETTINGS),
-                /*newValue=*/colorInversionTile);
+                /*newValue=*/colorInversionTile, userState.mUserId);
 
         Set<String> expected = Set.of(daltonizerTile, colorInversionTile);
         assertThat(readStringsFromSetting(ShortcutUtils.convertToKey(QUICK_SETTINGS)))
@@ -1677,11 +1685,18 @@
     }
 
     @Test
-    @DisableFlags(android.view.accessibility.Flags.FLAG_A11Y_QS_SHORTCUT)
-    public void restoreShortcutTargets_qs_a11yQsTargetsNotRestored() {
-        // TODO: remove the assumption when we fix b/381294327
+    @DisableFlags({android.view.accessibility.Flags.FLAG_A11Y_QS_SHORTCUT,
+            android.view.accessibility.Flags.FLAG_RESTORE_A11Y_SECURE_SETTINGS_ON_HSUM_DEVICE})
+    public void restoreShortcutTargetsAssumeUser0_qs_a11yQsTargetsNotRestored() {
         assumeTrue("The test is setup to run as a user 0",
                 mTestableContext.getUserId() == UserHandle.USER_SYSTEM);
+        restoreShortcutTargets_qs_a11yQsTargetsNotRestored();
+    }
+
+    @Test
+    @DisableFlags(android.view.accessibility.Flags.FLAG_A11Y_QS_SHORTCUT)
+    @EnableFlags(android.view.accessibility.Flags.FLAG_RESTORE_A11Y_SECURE_SETTINGS_ON_HSUM_DEVICE)
+    public void restoreShortcutTargets_qs_a11yQsTargetsNotRestored() {
         String daltonizerTile =
                 AccessibilityShortcutController.DALTONIZER_COMPONENT_NAME.flattenToString();
         String colorInversionTile =
@@ -1694,7 +1709,7 @@
 
         broadcastSettingRestored(
                 ShortcutUtils.convertToKey(QUICK_SETTINGS),
-                /*newValue=*/colorInversionTile);
+                /*newValue=*/colorInversionTile, userState.mUserId);
 
         Set<String> expected = Set.of(daltonizerTile);
         assertThat(readStringsFromSetting(ShortcutUtils.convertToKey(QUICK_SETTINGS)))
@@ -1762,10 +1777,16 @@
     }
 
     @Test
-    public void restoreShortcutTargets_hardware_targetsMerged() {
-        // TODO: remove the assumption when we fix b/381294327
+    @DisableFlags(android.view.accessibility.Flags.FLAG_RESTORE_A11Y_SECURE_SETTINGS_ON_HSUM_DEVICE)
+    public void restoreShortcutTargetsAssumeUser0_hardware_targetsMerged() {
         assumeTrue("The test is setup to run as a user 0",
                 mTestableContext.getUserId() == UserHandle.USER_SYSTEM);
+        restoreShortcutTargets_hardware_targetsMerged();
+    }
+
+    @Test
+    @EnableFlags(android.view.accessibility.Flags.FLAG_RESTORE_A11Y_SECURE_SETTINGS_ON_HSUM_DEVICE)
+    public void restoreShortcutTargets_hardware_targetsMerged() {
         mFakePermissionEnforcer.grant(Manifest.permission.MANAGE_ACCESSIBILITY);
         final String servicePrevious = TARGET_ALWAYS_ON_A11Y_SERVICE.flattenToString();
         final String otherPrevious = TARGET_MAGNIFICATION;
@@ -1779,7 +1800,7 @@
 
         broadcastSettingRestored(
                 ShortcutUtils.convertToKey(HARDWARE),
-                /*newValue=*/serviceRestored);
+                /*newValue=*/serviceRestored, userState.mUserId);
 
         final Set<String> expected = Set.of(servicePrevious, otherPrevious, serviceRestored);
         assertThat(readStringsFromSetting(ShortcutUtils.convertToKey(HARDWARE)))
@@ -1789,10 +1810,16 @@
     }
 
     @Test
-    public void restoreShortcutTargets_hardware_alreadyHadDefaultService_doesNotClear() {
-        // TODO: remove the assumption when we fix b/381294327
+    @DisableFlags(android.view.accessibility.Flags.FLAG_RESTORE_A11Y_SECURE_SETTINGS_ON_HSUM_DEVICE)
+    public void restoreShortcutTargetsAssumeUser0_hardware_alreadyHadDefaultService_doesNotClear() {
         assumeTrue("The test is setup to run as a user 0",
                 mTestableContext.getUserId() == UserHandle.USER_SYSTEM);
+        restoreShortcutTargets_hardware_alreadyHadDefaultService_doesNotClear();
+    }
+
+    @Test
+    @EnableFlags(android.view.accessibility.Flags.FLAG_RESTORE_A11Y_SECURE_SETTINGS_ON_HSUM_DEVICE)
+    public void restoreShortcutTargets_hardware_alreadyHadDefaultService_doesNotClear() {
         final String serviceDefault = TARGET_STANDARD_A11Y_SERVICE_NAME;
         mTestableContext.getOrCreateTestableResources().addOverride(
                 R.string.config_defaultAccessibilityService, serviceDefault);
@@ -1807,7 +1834,7 @@
 
         broadcastSettingRestored(
                 Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE,
-                /*newValue=*/serviceDefault);
+                /*newValue=*/serviceDefault, userState.mUserId);
 
         final Set<String> expected = Set.of(serviceDefault);
         assertThat(readStringsFromSetting(ShortcutUtils.convertToKey(HARDWARE)))
@@ -1817,10 +1844,16 @@
     }
 
     @Test
-    public void restoreShortcutTargets_hardware_didNotHaveDefaultService_clearsDefaultService() {
-        // TODO: remove the assumption when we fix b/381294327
+    @DisableFlags(android.view.accessibility.Flags.FLAG_RESTORE_A11Y_SECURE_SETTINGS_ON_HSUM_DEVICE)
+    public void restoreShortcutTargetsAsUser0_hardware_noDefaultService_clearsDefaultService() {
         assumeTrue("The test is setup to run as a user 0",
                 mTestableContext.getUserId() == UserHandle.USER_SYSTEM);
+        restoreShortcutTargets_hardware_didNotHaveDefaultService_clearsDefaultService();
+    }
+
+    @Test
+    @EnableFlags(android.view.accessibility.Flags.FLAG_RESTORE_A11Y_SECURE_SETTINGS_ON_HSUM_DEVICE)
+    public void restoreShortcutTargets_hardware_didNotHaveDefaultService_clearsDefaultService() {
         final String serviceDefault = TARGET_STANDARD_A11Y_SERVICE_NAME;
         final String serviceRestored = TARGET_ALWAYS_ON_A11Y_SERVICE.flattenToString();
         // Restored value from the broadcast contains both default and non-default service.
@@ -1833,7 +1866,7 @@
         setupShortcutTargetServices(userState);
 
         broadcastSettingRestored(ShortcutUtils.convertToKey(HARDWARE),
-                /*newValue=*/combinedRestored);
+                /*newValue=*/combinedRestored, userState.mUserId);
 
         // The default service is cleared from the final restored value.
         final Set<String> expected = Set.of(serviceRestored);
@@ -1844,10 +1877,16 @@
     }
 
     @Test
-    public void restoreShortcutTargets_hardware_nullSetting_clearsDefaultService() {
-        // TODO: remove the assumption when we fix b/381294327
+    @DisableFlags(android.view.accessibility.Flags.FLAG_RESTORE_A11Y_SECURE_SETTINGS_ON_HSUM_DEVICE)
+    public void restoreShortcutTargetsAssumeUser0_hardware_nullSetting_clearsDefaultService() {
         assumeTrue("The test is setup to run as a user 0",
                 mTestableContext.getUserId() == UserHandle.USER_SYSTEM);
+        restoreShortcutTargets_hardware_nullSetting_clearsDefaultService();
+    }
+
+    @Test
+    @EnableFlags(android.view.accessibility.Flags.FLAG_RESTORE_A11Y_SECURE_SETTINGS_ON_HSUM_DEVICE)
+    public void restoreShortcutTargets_hardware_nullSetting_clearsDefaultService() {
         final String serviceDefault = TARGET_STANDARD_A11Y_SERVICE_NAME;
         final String serviceRestored = TARGET_ALWAYS_ON_A11Y_SERVICE.flattenToString();
         // Restored value from the broadcast contains both default and non-default service.
@@ -1864,7 +1903,7 @@
         putShortcutSettingForUser(HARDWARE, null, userState.mUserId);
 
         broadcastSettingRestored(ShortcutUtils.convertToKey(HARDWARE),
-                /*newValue=*/combinedRestored);
+                /*newValue=*/combinedRestored, userState.mUserId);
 
         // The default service is cleared from the final restored value.
         final Set<String> expected = Set.of(serviceRestored);
@@ -2332,12 +2371,12 @@
                 setting, mA11yms.getCurrentUserIdLocked(), strings, str -> str);
     }
 
-    private void broadcastSettingRestored(String setting, String newValue) {
+    private void broadcastSettingRestored(String setting, String newValue, @UserIdInt int userId) {
         Intent intent = new Intent(Intent.ACTION_SETTING_RESTORED)
                 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY)
                 .putExtra(Intent.EXTRA_SETTING_NAME, setting)
                 .putExtra(Intent.EXTRA_SETTING_NEW_VALUE, newValue);
-        sendBroadcastToAccessibilityManagerService(intent);
+        sendBroadcastToAccessibilityManagerService(intent, userId);
         mTestableLooper.processAllMessages();
     }
 
@@ -2421,12 +2460,24 @@
         userState.updateTileServiceMapForAccessibilityServiceLocked();
     }
 
-    private void sendBroadcastToAccessibilityManagerService(Intent intent) {
+    private void sendBroadcastToAccessibilityManagerService(Intent intent, @UserIdInt int userId) {
         if (!mTestableContext.getBroadcastReceivers().containsKey(intent.getAction())) {
             return;
         }
         mTestableContext.getBroadcastReceivers().get(intent.getAction()).forEach(
-                broadcastReceiver -> broadcastReceiver.onReceive(mTestableContext, intent));
+                broadcastReceiver -> {
+                    BroadcastReceiver.PendingResult result = mock(
+                            BroadcastReceiver.PendingResult.class);
+                    try {
+                        FieldSetter.setField(result,
+                                BroadcastReceiver.PendingResult.class.getDeclaredField(
+                                        "mSendingUser"), userId);
+                    } catch (NoSuchFieldException e) {
+                        // do nothing
+                    }
+                    broadcastReceiver.setPendingResult(result);
+                    broadcastReceiver.onReceive(mTestableContext, intent);
+                });
     }
 
     public static class FakeInputFilter extends AccessibilityInputFilter {
@@ -2449,6 +2500,7 @@
         A11yTestableContext(Context base) {
             super(base);
             mMockContext = mock(Context.class);
+
         }
 
         @Override
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
index c878799..4ef602f 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
@@ -25,6 +25,7 @@
 import static com.android.server.wm.WindowManagerInternal.AccessibilityControllerInternal.UiChangesForAccessibilityCallbacks;
 
 import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -34,6 +35,7 @@
 import static org.mockito.ArgumentMatchers.anyFloat;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.floatThat;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.ArgumentMatchers.nullable;
 import static org.mockito.Mockito.doAnswer;
@@ -50,9 +52,11 @@
 import android.animation.ValueAnimator;
 import android.content.Context;
 import android.content.pm.PackageManager;
+import android.content.res.Resources;
 import android.graphics.PointF;
 import android.graphics.Rect;
 import android.graphics.Region;
+import android.hardware.display.DisplayManager;
 import android.hardware.display.DisplayManagerInternal;
 import android.os.Looper;
 import android.os.RemoteException;
@@ -60,6 +64,7 @@
 import android.provider.Settings;
 import android.test.mock.MockContentResolver;
 import android.testing.DexmakerShareClassLoaderRule;
+import android.util.DisplayMetrics;
 import android.view.Display;
 import android.view.DisplayInfo;
 import android.view.accessibility.IRemoteMagnificationAnimationCallback;
@@ -79,6 +84,8 @@
 import com.android.server.input.InputManagerInternal;
 import com.android.server.wm.WindowManagerInternal;
 
+import com.google.common.truth.Expect;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
@@ -96,6 +103,9 @@
 @RunWith(AndroidJUnit4.class)
 public class MagnificationControllerTest {
 
+    @Rule
+    public final Expect expect = Expect.create();
+
     private static final int TEST_DISPLAY = Display.DEFAULT_DISPLAY;
     private static final int TEST_SERVICE_ID = 1;
     private static final Region INITIAL_SCREEN_MAGNIFICATION_REGION =
@@ -119,6 +129,8 @@
     @Mock
     private Context mContext;
     @Mock
+    private Resources mResources;
+    @Mock
     private PackageManager mPackageManager;
 
     @Mock
@@ -156,6 +168,7 @@
 
     @Mock
     private DisplayManagerInternal mDisplayManagerInternal;
+    private Display mDisplay;
 
     @Mock
     private Scroller mMockScroller;
@@ -210,6 +223,12 @@
         LocalServices.removeServiceForTest(DisplayManagerInternal.class);
         LocalServices.addService(DisplayManagerInternal.class, mDisplayManagerInternal);
 
+        DisplayManager displayManager = new DisplayManager(mContext);
+        mDisplay = displayManager.getDisplay(TEST_DISPLAY);
+        when(mContext.getSystemServiceName(DisplayManager.class)).thenReturn(
+                Context.DISPLAY_SERVICE);
+        when(mContext.getSystemService(DisplayManager.class)).thenReturn(displayManager);
+
         mScreenMagnificationController =
                 spy(
                         new FullScreenMagnificationController(
@@ -686,16 +705,19 @@
 
         float currentScale = mScreenMagnificationController.getScale(TEST_DISPLAY);
         while (currentScale < SCALE_MAX_VALUE) {
-            assertThat(mMagnificationController.scaleMagnificationByStep(TEST_DISPLAY,
-                    MagnificationController.ZOOM_DIRECTION_IN)).isTrue();
+            mMagnificationController.scaleMagnificationByStep(TEST_DISPLAY,
+                    MagnificationController.ZOOM_DIRECTION_IN);
             final float nextScale = mScreenMagnificationController.getScale(TEST_DISPLAY);
             assertThat(nextScale).isGreaterThan(currentScale);
             currentScale = nextScale;
         }
 
         assertThat(currentScale).isEqualTo(SCALE_MAX_VALUE);
-        assertThat(mMagnificationController.scaleMagnificationByStep(TEST_DISPLAY,
-                MagnificationController.ZOOM_DIRECTION_IN)).isFalse();
+        // Trying to scale further does not change the scale.
+        mMagnificationController.scaleMagnificationByStep(TEST_DISPLAY,
+                MagnificationController.ZOOM_DIRECTION_IN);
+        final float finalScale = mScreenMagnificationController.getScale(TEST_DISPLAY);
+        assertThat(finalScale).isEqualTo(currentScale);
     }
 
     @Test
@@ -706,16 +728,19 @@
 
         float currentScale = mScreenMagnificationController.getScale(TEST_DISPLAY);
         while (currentScale > SCALE_MIN_VALUE) {
-            assertThat(mMagnificationController.scaleMagnificationByStep(TEST_DISPLAY,
-                    MagnificationController.ZOOM_DIRECTION_OUT)).isTrue();
+            mMagnificationController.scaleMagnificationByStep(TEST_DISPLAY,
+                    MagnificationController.ZOOM_DIRECTION_OUT);
             final float nextScale = mScreenMagnificationController.getScale(TEST_DISPLAY);
             assertThat(nextScale).isLessThan(currentScale);
             currentScale = nextScale;
         }
 
         assertThat(currentScale).isEqualTo(SCALE_MIN_VALUE);
-        assertThat(mMagnificationController.scaleMagnificationByStep(TEST_DISPLAY,
-                MagnificationController.ZOOM_DIRECTION_OUT)).isFalse();
+        // Trying to scale further does not change the scale.
+        mMagnificationController.scaleMagnificationByStep(TEST_DISPLAY,
+                MagnificationController.ZOOM_DIRECTION_OUT);
+        final float finalScale = mScreenMagnificationController.getScale(TEST_DISPLAY);
+        assertThat(finalScale).isEqualTo(currentScale);
     }
 
     @Test
@@ -740,6 +765,121 @@
     }
 
     @Test
+    public void panMagnificationByStep_fullscreenMode_stepSizeAtScale2() throws RemoteException {
+        setMagnificationEnabled(MODE_FULLSCREEN);
+        // At scale 2.0f, each step should be about 40 dpi.
+        mMagnificationController.onPerformScaleAction(TEST_DISPLAY, 2.0f, false);
+        reset(mScreenMagnificationController);
+
+        testFullscreenMagnificationPanWithStepSize(40.0f);
+    }
+
+    @Test
+    public void panMagnificationByStep_fullscreenMode_stepSizeAtScale8() throws RemoteException {
+        setMagnificationEnabled(MODE_FULLSCREEN);
+        // At scale 8.0f, each step should be about 27 dpi.
+        mMagnificationController.onPerformScaleAction(TEST_DISPLAY, 8.0f, false);
+        reset(mScreenMagnificationController);
+
+        testFullscreenMagnificationPanWithStepSize(27.0f);
+    }
+
+    @Test
+    public void panMagnificationByStep_windowMode_stepSizeAtScale2() throws RemoteException {
+        mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 2.0f, 100f, 200f);
+
+        testWindowMagnificationPanWithStepSize(40.0f);
+    }
+
+    @Test
+    public void panMagnificationByStep_windowMode_stepSizeAtScale8() throws RemoteException {
+        setMagnificationEnabled(MODE_WINDOW);
+        // At scale 8.0f, each step should be about 27.
+        mMagnificationController.onPerformScaleAction(TEST_DISPLAY, 8.0f, false);
+        reset(mMagnificationConnectionManager);
+
+        testWindowMagnificationPanWithStepSize(27.0f);
+    }
+
+    @Test
+    public void panMagnificationByStep_fullscreenMode_reachesRightEdgeOfScreen()
+            throws RemoteException {
+        setMagnificationEnabled(MODE_FULLSCREEN);
+        // At scale 2.0f, each step should be about 40.
+        mMagnificationController.onPerformScaleAction(TEST_DISPLAY, DEFAULT_SCALE, false);
+        reset(mScreenMagnificationController);
+
+        float currentCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY);
+        float currentCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY);
+
+        DisplayMetrics metrics = new DisplayMetrics();
+        mDisplay.getMetrics(metrics);
+        float expectedStep = 40.0f * metrics.density;
+
+        // Move right, eventually we should reach the edge.
+        int maxNumSteps = (int) (metrics.widthPixels / expectedStep) + 1;
+        int numSteps = 0;
+        while (numSteps < maxNumSteps) {
+            mMagnificationController.panMagnificationByStep(TEST_DISPLAY,
+                    MagnificationController.PAN_DIRECTION_RIGHT);
+            float newCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY);
+            float newCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY);
+            assertThat(currentCenterY).isEqualTo(newCenterY);
+
+            assertThat(newCenterX).isAtLeast(currentCenterX);
+            if (newCenterX == currentCenterX) {
+                break;
+            }
+
+            currentCenterX = newCenterX;
+            currentCenterY = newCenterY;
+            numSteps++;
+        }
+        assertWithMessage("Still not at edge after panning right " + numSteps
+                + " steps. Current position: " + currentCenterX + "," + currentCenterY)
+                .that(numSteps).isLessThan(maxNumSteps);
+    }
+
+    @Test
+    public void panMagnificationByStep_fullscreenMode_reachesBottomEdgeOfScreen()
+            throws RemoteException {
+        setMagnificationEnabled(MODE_FULLSCREEN);
+        // At scale 2.0f, each step should be about 40.
+        mMagnificationController.onPerformScaleAction(TEST_DISPLAY, DEFAULT_SCALE, false);
+        reset(mScreenMagnificationController);
+
+        float currentCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY);
+        float currentCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY);
+
+        DisplayMetrics metrics = new DisplayMetrics();
+        mDisplay.getMetrics(metrics);
+        float expectedStep = 40.0f * metrics.density;
+
+        // Move down, eventually we should reach the edge.
+        int maxNumSteps = (int) (metrics.heightPixels / expectedStep) + 1;
+        int numSteps = 0;
+        while (numSteps < maxNumSteps) {
+            mMagnificationController.panMagnificationByStep(TEST_DISPLAY,
+                    MagnificationController.PAN_DIRECTION_DOWN);
+            float newCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY);
+            float newCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY);
+            assertThat(currentCenterX).isEqualTo(newCenterX);
+
+            assertThat(newCenterY).isAtLeast(currentCenterY);
+            if (newCenterY == currentCenterY) {
+                break;
+            }
+
+            currentCenterX = newCenterX;
+            currentCenterY = newCenterY;
+            numSteps++;
+        }
+        assertWithMessage("Still not at edge after panning down "
+                + numSteps + " steps. Current position: " + currentCenterX + "," + currentCenterY)
+                .that(numSteps).isLessThan(maxNumSteps);
+    }
+
+    @Test
     public void enableWindowMode_notifyMagnificationChanged() throws RemoteException {
         setMagnificationEnabled(MODE_WINDOW);
 
@@ -1425,6 +1565,91 @@
         return captor.getValue();
     }
 
+    private void testFullscreenMagnificationPanWithStepSize(float expectedStep) {
+        DisplayMetrics metrics = new DisplayMetrics();
+        mDisplay.getMetrics(metrics);
+        expectedStep *= metrics.density;
+
+        float currentCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY);
+        float currentCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY);
+
+        // Move right.
+        mMagnificationController.panMagnificationByStep(TEST_DISPLAY,
+                MagnificationController.PAN_DIRECTION_RIGHT);
+        float newCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY);
+        float newCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY);
+        expect.that(currentCenterX).isLessThan(newCenterX);
+        expect.that(newCenterX - currentCenterX).isWithin(0.01f).of(expectedStep);
+        expect.that(currentCenterY).isEqualTo(newCenterY);
+
+        currentCenterX = newCenterX;
+        currentCenterY = newCenterY;
+
+        // Move left.
+        mMagnificationController.panMagnificationByStep(TEST_DISPLAY,
+                MagnificationController.PAN_DIRECTION_LEFT);
+        newCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY);
+        newCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY);
+        expect.that(currentCenterX).isGreaterThan(newCenterX);
+        expect.that(currentCenterX - newCenterX).isWithin(0.01f).of(expectedStep);
+        expect.that(currentCenterY).isEqualTo(newCenterY);
+
+        currentCenterX = newCenterX;
+        currentCenterY = newCenterY;
+
+        // Move down.
+        mMagnificationController.panMagnificationByStep(TEST_DISPLAY,
+                MagnificationController.PAN_DIRECTION_DOWN);
+        newCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY);
+        newCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY);
+        expect.that(currentCenterX).isEqualTo(newCenterX);
+        expect.that(currentCenterY).isLessThan(newCenterY);
+        expect.that(newCenterY - currentCenterY).isWithin(0.1f).of(expectedStep);
+
+        currentCenterX = newCenterX;
+        currentCenterY = newCenterY;
+
+        // Move up.
+        mMagnificationController.panMagnificationByStep(TEST_DISPLAY,
+                MagnificationController.PAN_DIRECTION_UP);
+        newCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY);
+        newCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY);
+        expect.that(currentCenterX).isEqualTo(newCenterX);
+        expect.that(currentCenterY).isGreaterThan(newCenterY);
+        expect.that(currentCenterY - newCenterY).isWithin(0.01f).of(expectedStep);
+    }
+
+    private void testWindowMagnificationPanWithStepSize(float expectedStepDip)
+            throws RemoteException {
+        DisplayMetrics metrics = new DisplayMetrics();
+        mDisplay.getMetrics(metrics);
+        final float expectedStep = expectedStepDip * metrics.density;
+
+        // Move right.
+        mMagnificationController.panMagnificationByStep(TEST_DISPLAY,
+                MagnificationController.PAN_DIRECTION_RIGHT);
+        verify(mMockConnection.getConnection()).moveWindowMagnifier(eq(TEST_DISPLAY),
+                floatThat(step -> Math.abs(step - expectedStep) < 0.0001), eq(0.0f));
+
+        // Move left.
+        mMagnificationController.panMagnificationByStep(TEST_DISPLAY,
+                MagnificationController.PAN_DIRECTION_LEFT);
+        verify(mMockConnection.getConnection()).moveWindowMagnifier(eq(TEST_DISPLAY),
+                floatThat(step -> Math.abs(expectedStep - step) < 0.0001), eq(0.0f));
+
+        // Move down.
+        mMagnificationController.panMagnificationByStep(TEST_DISPLAY,
+                MagnificationController.PAN_DIRECTION_DOWN);
+        verify(mMockConnection.getConnection()).moveWindowMagnifier(eq(TEST_DISPLAY),
+                eq(0.0f), floatThat(step -> Math.abs(expectedStep - step) < 0.0001));
+
+        // Move up.
+        mMagnificationController.panMagnificationByStep(TEST_DISPLAY,
+                MagnificationController.PAN_DIRECTION_UP);
+        verify(mMockConnection.getConnection()).moveWindowMagnifier(eq(TEST_DISPLAY),
+                eq(0.0f), floatThat(step -> Math.abs(expectedStep - step) < 0.0001));
+    }
+
     private static class WindowMagnificationMgrCallbackDelegate implements
             MagnificationConnectionManager.Callback {
         private MagnificationConnectionManager.Callback mCallback;
diff --git a/services/tests/servicestests/src/com/android/server/app/GameManagerServiceSettingsTests.java b/services/tests/servicestests/src/com/android/server/app/GameManagerServiceSettingsTests.java
index fde3422..17f5ebb 100644
--- a/services/tests/servicestests/src/com/android/server/app/GameManagerServiceSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/app/GameManagerServiceSettingsTests.java
@@ -130,9 +130,9 @@
         assertEquals(GameManager.GAME_MODE_STANDARD, settings.getGameModeLocked(PACKAGE_NAME_4));
 
         // test game mode configs
-        assertNull(settings.getConfigOverride(PACKAGE_NAME_1));
-        assertNull(settings.getConfigOverride(PACKAGE_NAME_3));
-        GamePackageConfiguration config = settings.getConfigOverride(PACKAGE_NAME_2);
+        assertNull(settings.getConfigOverrideLocked(PACKAGE_NAME_1));
+        assertNull(settings.getConfigOverrideLocked(PACKAGE_NAME_3));
+        GamePackageConfiguration config = settings.getConfigOverrideLocked(PACKAGE_NAME_2);
         assertNotNull(config);
 
         assertNull(config.getGameModeConfiguration(GameManager.GAME_MODE_STANDARD));
@@ -152,7 +152,7 @@
         assertEquals(batteryConfig.getFpsStr(), GameModeConfiguration.DEFAULT_FPS);
         assertFalse(batteryConfig.getUseAngle());
 
-        config = settings.getConfigOverride(PACKAGE_NAME_4);
+        config = settings.getConfigOverrideLocked(PACKAGE_NAME_4);
         assertNotNull(config);
         GameModeConfiguration customConfig = config.getGameModeConfiguration(
                 GameManager.GAME_MODE_CUSTOM);
@@ -177,7 +177,7 @@
         GameManagerSettings settings = new GameManagerSettings(context.getFilesDir());
         assertTrue(settings.readPersistentDataLocked());
 
-        final GamePackageConfiguration config = settings.getConfigOverride(PACKAGE_NAME_1);
+        final GamePackageConfiguration config = settings.getConfigOverrideLocked(PACKAGE_NAME_1);
         assertNotNull(config);
         final GameModeConfiguration batteryConfig = config.getGameModeConfiguration(
                 GameManager.GAME_MODE_BATTERY);
@@ -218,7 +218,7 @@
         assertEquals(2, settings.getGameModeLocked(PACKAGE_NAME_2));
         assertEquals(3, settings.getGameModeLocked(PACKAGE_NAME_3));
 
-        final GamePackageConfiguration config = settings.getConfigOverride(PACKAGE_NAME_2);
+        final GamePackageConfiguration config = settings.getConfigOverrideLocked(PACKAGE_NAME_2);
         assertNotNull(config);
         final GameModeConfiguration batteryConfig = config.getGameModeConfiguration(
                 GameManager.GAME_MODE_BATTERY);
@@ -248,7 +248,7 @@
         GameModeConfiguration batteryConfig = config.getOrAddDefaultGameModeConfiguration(
                 GameManager.GAME_MODE_BATTERY);
         batteryConfig.setScaling(0.77f);
-        settings.setConfigOverride(PACKAGE_NAME_2, config);
+        settings.setConfigOverrideLocked(PACKAGE_NAME_2, config);
 
         // set config for app4
         config = new GamePackageConfiguration(PACKAGE_NAME_4);
@@ -256,15 +256,15 @@
                 GameManager.GAME_MODE_CUSTOM);
         customConfig.setScaling(0.4f);
         customConfig.setFpsStr("30");
-        settings.setConfigOverride(PACKAGE_NAME_4, config);
+        settings.setConfigOverrideLocked(PACKAGE_NAME_4, config);
 
         settings.writePersistentDataLocked();
 
         // clear the settings in memory
-        settings.removeGame(PACKAGE_NAME_1);
-        settings.removeGame(PACKAGE_NAME_2);
-        settings.removeGame(PACKAGE_NAME_3);
-        settings.removeGame(PACKAGE_NAME_4);
+        settings.removeGameLocked(PACKAGE_NAME_1);
+        settings.removeGameLocked(PACKAGE_NAME_2);
+        settings.removeGameLocked(PACKAGE_NAME_3);
+        settings.removeGameLocked(PACKAGE_NAME_4);
 
         // read back in and verify
         assertTrue(settings.readPersistentDataLocked());
@@ -273,9 +273,9 @@
         assertEquals(1, settings.getGameModeLocked(PACKAGE_NAME_3));
         assertEquals(1, settings.getGameModeLocked(PACKAGE_NAME_4));
 
-        config = settings.getConfigOverride(PACKAGE_NAME_1);
+        config = settings.getConfigOverrideLocked(PACKAGE_NAME_1);
         assertNull(config);
-        config = settings.getConfigOverride(PACKAGE_NAME_2);
+        config = settings.getConfigOverrideLocked(PACKAGE_NAME_2);
         assertNotNull(config);
         batteryConfig = config.getGameModeConfiguration(GameManager.GAME_MODE_BATTERY);
         assertNotNull(batteryConfig);
@@ -292,7 +292,7 @@
         assertEquals(performanceConfig.getFpsStr(), "60");
         assertTrue(performanceConfig.getUseAngle());
 
-        config = settings.getConfigOverride(PACKAGE_NAME_4);
+        config = settings.getConfigOverrideLocked(PACKAGE_NAME_4);
         assertNotNull(config);
         customConfig = config.getGameModeConfiguration(GameManager.GAME_MODE_CUSTOM);
         assertNotNull(customConfig);
diff --git a/services/tests/servicestests/src/com/android/server/autofill/PresentationEventLoggerTest.java b/services/tests/servicestests/src/com/android/server/autofill/PresentationEventLoggerTest.java
index 75258f0..d2db999 100644
--- a/services/tests/servicestests/src/com/android/server/autofill/PresentationEventLoggerTest.java
+++ b/services/tests/servicestests/src/com/android/server/autofill/PresentationEventLoggerTest.java
@@ -15,6 +15,8 @@
  */
 package com.android.server.autofill;
 
+import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_SUGGESTION_FILTER_OUT;
+import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_VIEW_CHANGED;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -129,4 +131,57 @@
         assertThat(event).isNotNull();
         assertThat(event.mDisplayPresentationType).isEqualTo(3);
     }
+
+    @Test
+    public void testNoSuggestionsTextFiltered() {
+        PresentationStatsEventLogger pEventLogger =
+                PresentationStatsEventLogger.createPresentationLog(1, 1, 1);
+        AutofillId id = new AutofillId(13);
+        AutofillValue initialValue = AutofillValue.forText("hello");
+        pEventLogger.startNewEvent();
+        pEventLogger.maybeSetFocusedId(id);
+        pEventLogger.maybeSetNoPresentationEventReasonSuggestionsFiltered(initialValue);
+
+        PresentationStatsEventLogger.PresentationStatsEventInternal event =
+                pEventLogger.getInternalEvent().get();
+        assertThat(event).isNotNull();
+        int NO_SUGGESTIONS =
+                AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_SUGGESTION_FILTER_OUT;
+        assertThat(event.mNoPresentationReason).isEqualTo(NO_SUGGESTIONS);
+    }
+
+    @Test
+    public void testSuggestionsTextNotFiltered() {
+        PresentationStatsEventLogger pEventLogger =
+                PresentationStatsEventLogger.createPresentationLog(1, 1, 1);
+        AutofillId id = new AutofillId(13);
+        AutofillValue initialValue = null;
+        pEventLogger.startNewEvent();
+        pEventLogger.maybeSetFocusedId(id);
+        pEventLogger.maybeSetNoPresentationEventReasonSuggestionsFiltered(initialValue);
+
+        PresentationStatsEventLogger.PresentationStatsEventInternal event =
+                pEventLogger.getInternalEvent().get();
+        assertThat(event).isNotNull();
+        assertThat(event.mNoPresentationReason).isNotEqualTo(
+                AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_SUGGESTION_FILTER_OUT);
+    }
+
+    @Test
+    public void testNotShownReasonNotOverridden() {
+
+        PresentationStatsEventLogger pEventLogger =
+                PresentationStatsEventLogger.createPresentationLog(1, 1, 1);
+
+        pEventLogger.startNewEvent();
+        pEventLogger.maybeSetNoPresentationEventReason(AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_VIEW_CHANGED);
+        // Not allowed - no op
+        pEventLogger.maybeSetNoPresentationEventReasonIfNoReasonExists(
+                AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_SUGGESTION_FILTER_OUT);
+
+        PresentationStatsEventLogger.PresentationStatsEventInternal event =
+                pEventLogger.getInternalEvent().get();
+        assertThat(event).isNotNull();
+        assertThat(event.mNoPresentationReason).isEqualTo(AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_VIEW_CHANGED);
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
index a0f2395..d70ffd2 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
@@ -159,7 +159,7 @@
     /**
      * Test for the first launch path, no settings file available.
      */
-    public void testFirstInitialize() {
+    public void FirstInitialize() {
         assertResetTimes(START_TIME, START_TIME + INTERVAL);
     }
 
@@ -167,7 +167,7 @@
      * Test for {@link ShortcutService#getLastResetTimeLocked()} and
      * {@link ShortcutService#getNextResetTimeLocked()}.
      */
-    public void testUpdateAndGetNextResetTimeLocked() {
+    public void UpdateAndGetNextResetTimeLocked() {
         assertResetTimes(START_TIME, START_TIME + INTERVAL);
 
         // Advance clock.
@@ -196,7 +196,7 @@
     /**
      * Test for the restoration from saved file.
      */
-    public void testInitializeFromSavedFile() {
+    public void InitializeFromSavedFile() {
 
         mInjectedCurrentTimeMillis = START_TIME + 4 * INTERVAL + 50;
         assertResetTimes(START_TIME + 4 * INTERVAL, START_TIME + 5 * INTERVAL);
@@ -220,7 +220,7 @@
         // TODO Add various broken cases.
     }
 
-    public void testLoadConfig() {
+    public void LoadConfig() {
         mService.updateConfigurationLocked(
                 ConfigConstants.KEY_RESET_INTERVAL_SEC + "=123,"
                         + ConfigConstants.KEY_MAX_SHORTCUTS + "=4,"
@@ -261,22 +261,22 @@
     // === Test for app side APIs ===
 
     /** Test for {@link android.content.pm.ShortcutManager#getMaxShortcutCountForActivity()} */
-    public void testGetMaxDynamicShortcutCount() {
+    public void GetMaxDynamicShortcutCount() {
         assertEquals(MAX_SHORTCUTS, mManager.getMaxShortcutCountForActivity());
     }
 
     /** Test for {@link android.content.pm.ShortcutManager#getRemainingCallCount()} */
-    public void testGetRemainingCallCount() {
+    public void GetRemainingCallCount() {
         assertEquals(MAX_UPDATES_PER_INTERVAL, mManager.getRemainingCallCount());
     }
 
-    public void testGetIconMaxDimensions() {
+    public void GetIconMaxDimensions() {
         assertEquals(MAX_ICON_DIMENSION, mManager.getIconMaxWidth());
         assertEquals(MAX_ICON_DIMENSION, mManager.getIconMaxHeight());
     }
 
     /** Test for {@link android.content.pm.ShortcutManager#getRateLimitResetTime()} */
-    public void testGetRateLimitResetTime() {
+    public void GetRateLimitResetTime() {
         assertEquals(START_TIME + INTERVAL, mManager.getRateLimitResetTime());
 
         mInjectedCurrentTimeMillis = START_TIME + 4 * INTERVAL + 50;
@@ -284,7 +284,7 @@
         assertEquals(START_TIME + 5 * INTERVAL, mManager.getRateLimitResetTime());
     }
 
-    public void testSetDynamicShortcuts() {
+    public void SetDynamicShortcuts() {
         setCaller(CALLING_PACKAGE_1, USER_0);
 
         final Icon icon1 = Icon.createWithResource(getTestContext(), R.drawable.icon1);
@@ -354,7 +354,7 @@
         });
     }
 
-    public void testAddDynamicShortcuts() {
+    public void AddDynamicShortcuts() {
         setCaller(CALLING_PACKAGE_1, USER_0);
 
         final ShortcutInfo si1 = makeShortcut("shortcut1");
@@ -402,7 +402,7 @@
         });
     }
 
-    public void testPushDynamicShortcut() {
+    public void PushDynamicShortcut() {
         // Change the max number of shortcuts.
         mService.updateConfigurationLocked(ConfigConstants.KEY_MAX_SHORTCUTS + "=5,"
                 + ShortcutService.ConfigConstants.KEY_SAVE_DELAY_MILLIS + "=1");
@@ -543,7 +543,7 @@
                 eq(CALLING_PACKAGE_1), eq("s9"), eq(USER_0));
     }
 
-    public void testPushDynamicShortcut_CallsToUsageStatsManagerAreThrottled()
+    public void PushDynamicShortcut_CallsToUsageStatsManagerAreThrottled()
             throws InterruptedException {
         mService.updateConfigurationLocked(
                 ShortcutService.ConfigConstants.KEY_SAVE_DELAY_MILLIS + "=500");
@@ -594,7 +594,7 @@
                 eq(CALLING_PACKAGE_2), any(), eq(USER_0));
     }
 
-    public void testUnlimitedCalls() {
+    public void UnlimitedCalls() {
         setCaller(CALLING_PACKAGE_1, USER_0);
 
         final ShortcutInfo si1 = makeShortcut("shortcut1");
@@ -625,7 +625,7 @@
         assertEquals(3, mManager.getRemainingCallCount());
     }
 
-    public void testPublishWithNoActivity() {
+    public void PublishWithNoActivity() {
         // If activity is not explicitly set, use the default one.
 
         mRunningUsers.put(USER_10, true);
@@ -731,7 +731,7 @@
         });
     }
 
-    public void testPublishWithNoActivity_noMainActivityInPackage() {
+    public void PublishWithNoActivity_noMainActivityInPackage() {
         mRunningUsers.put(USER_10, true);
 
         runWithCaller(CALLING_PACKAGE_2, USER_10, () -> {
@@ -750,7 +750,7 @@
         });
     }
 
-    public void testDeleteDynamicShortcuts() {
+    public void DeleteDynamicShortcuts() {
         final ShortcutInfo si1 = makeShortcut("shortcut1");
         final ShortcutInfo si2 = makeShortcut("shortcut2");
         final ShortcutInfo si3 = makeShortcut("shortcut3");
@@ -791,7 +791,7 @@
         assertEquals(2, mManager.getRemainingCallCount());
     }
 
-    public void testDeleteAllDynamicShortcuts() {
+    public void DeleteAllDynamicShortcuts() {
         final ShortcutInfo si1 = makeShortcut("shortcut1");
         final ShortcutInfo si2 = makeShortcut("shortcut2");
         final ShortcutInfo si3 = makeShortcut("shortcut3");
@@ -820,7 +820,7 @@
         assertEquals(1, mManager.getRemainingCallCount());
     }
 
-    public void testIcons() throws IOException {
+    public void Icons() throws IOException {
         final Icon res32x32 = Icon.createWithResource(getTestContext(), R.drawable.black_32x32);
         final Icon res64x64 = Icon.createWithResource(getTestContext(), R.drawable.black_64x64);
         final Icon res512x512 = Icon.createWithResource(getTestContext(), R.drawable.black_512x512);
@@ -1034,7 +1034,7 @@
 */
     }
 
-    public void testCleanupDanglingBitmaps() throws Exception {
+    public void CleanupDanglingBitmaps() throws Exception {
         assertBitmapDirectories(USER_0, EMPTY_STRINGS);
         assertBitmapDirectories(USER_10, EMPTY_STRINGS);
 
@@ -1203,7 +1203,7 @@
                         maxSize));
     }
 
-    public void testShrinkBitmap() {
+    public void ShrinkBitmap() {
         checkShrinkBitmap(32, 32, R.drawable.black_512x512, 32);
         checkShrinkBitmap(511, 511, R.drawable.black_512x512, 511);
         checkShrinkBitmap(512, 512, R.drawable.black_512x512, 512);
@@ -1226,7 +1226,7 @@
         return out.getFile();
     }
 
-    public void testOpenIconFileForWrite() throws IOException {
+    public void OpenIconFileForWrite() throws IOException {
         mInjectedCurrentTimeMillis = 1000;
 
         final File p10_1_1 = openIconFileForWriteAndGetPath(10, CALLING_PACKAGE_1);
@@ -1300,7 +1300,7 @@
         assertFalse(p11_1_3.getName().contains("_"));
     }
 
-    public void testUpdateShortcuts() {
+    public void UpdateShortcuts() {
         runWithCaller(CALLING_PACKAGE_1, UserHandle.USER_SYSTEM, () -> {
             assertTrue(mManager.setDynamicShortcuts(list(
                     makeShortcut("s1"),
@@ -1431,7 +1431,7 @@
         });
     }
 
-    public void testUpdateShortcuts_icons() {
+    public void UpdateShortcuts_icons() {
         runWithCaller(CALLING_PACKAGE_1, UserHandle.USER_SYSTEM, () -> {
             assertTrue(mManager.setDynamicShortcuts(list(
                     makeShortcut("s1")
@@ -1525,7 +1525,7 @@
         });
     }
 
-    public void testShortcutManagerGetShortcuts_shortcutTypes() {
+    public void ShortcutManagerGetShortcuts_shortcutTypes() {
 
         // Create 3 manifest and 3 dynamic shortcuts
         addManifestShortcutResource(
@@ -1616,7 +1616,7 @@
         assertShortcutIds(mManager.getShortcuts(ShortcutManager.FLAG_MATCH_CACHED), "s1", "s2");
     }
 
-    public void testCachedShortcuts() {
+    public void CachedShortcuts() {
         runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
             assertTrue(mManager.setDynamicShortcuts(list(makeShortcut("s1"),
                     makeLongLivedShortcut("s2"), makeLongLivedShortcut("s3"),
@@ -1700,7 +1700,7 @@
                 "s2");
     }
 
-    public void testCachedShortcuts_accessShortcutsPermission() {
+    public void CachedShortcuts_accessShortcutsPermission() {
         runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
             assertTrue(mManager.setDynamicShortcuts(list(makeShortcut("s1"),
                     makeLongLivedShortcut("s2"), makeLongLivedShortcut("s3"),
@@ -1742,7 +1742,7 @@
         assertShortcutIds(mManager.getShortcuts(ShortcutManager.FLAG_MATCH_CACHED), "s3");
     }
 
-    public void testCachedShortcuts_canPassShortcutLimit() {
+    public void CachedShortcuts_canPassShortcutLimit() {
         // Change the max number of shortcuts.
         mService.updateConfigurationLocked(ConfigConstants.KEY_MAX_SHORTCUTS + "=4");
 
@@ -1780,7 +1780,7 @@
 
     // === Test for launcher side APIs ===
 
-    public void testGetShortcuts() {
+    public void GetShortcuts() {
 
         // Set up shortcuts.
 
@@ -1997,7 +1997,7 @@
                 "s1", "s3");
     }
 
-    public void testGetShortcuts_shortcutKinds() throws Exception {
+    public void GetShortcuts_shortcutKinds() throws Exception {
         // Create 3 manifest and 3 dynamic shortcuts
         addManifestShortcutResource(
                 new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
@@ -2108,7 +2108,7 @@
         });
     }
 
-    public void testGetShortcuts_resolveStrings() throws Exception {
+    public void GetShortcuts_resolveStrings() throws Exception {
         runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
             ShortcutInfo si = new ShortcutInfo.Builder(mClientContext)
                     .setId("id")
@@ -2156,7 +2156,7 @@
         });
     }
 
-    public void testGetShortcuts_personsFlag() {
+    public void GetShortcuts_personsFlag() {
         ShortcutInfo s = new ShortcutInfo.Builder(mClientContext, "id")
                 .setShortLabel("label")
                 .setActivity(new ComponentName(mClientContext, ShortcutActivity2.class))
@@ -2204,7 +2204,7 @@
     }
 
     // TODO resource
-    public void testGetShortcutInfo() {
+    public void GetShortcutInfo() {
         // Create shortcuts.
         setCaller(CALLING_PACKAGE_1);
         final ShortcutInfo s1_1 = makeShortcut(
@@ -2279,7 +2279,7 @@
         assertEquals("ABC", findById(list, "s1").getTitle());
     }
 
-    public void testPinShortcutAndGetPinnedShortcuts() {
+    public void PinShortcutAndGetPinnedShortcuts() {
         runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
             final ShortcutInfo s1_1 = makeShortcutWithTimestamp("s1", 1000);
             final ShortcutInfo s1_2 = makeShortcutWithTimestamp("s2", 2000);
@@ -2360,7 +2360,7 @@
      * This is similar to the above test, except it used "disable" instead of "remove".  It also
      * does "enable".
      */
-    public void testDisableAndEnableShortcuts() {
+    public void DisableAndEnableShortcuts() {
         runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
             final ShortcutInfo s1_1 = makeShortcutWithTimestamp("s1", 1000);
             final ShortcutInfo s1_2 = makeShortcutWithTimestamp("s2", 2000);
@@ -2485,7 +2485,7 @@
         });
     }
 
-    public void testDisableShortcuts_thenRepublish() {
+    public void DisableShortcuts_thenRepublish() {
         runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
             assertTrue(mManager.setDynamicShortcuts(list(
                     makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"))));
@@ -2555,7 +2555,7 @@
         });
     }
 
-    public void testPinShortcutAndGetPinnedShortcuts_multi() {
+    public void PinShortcutAndGetPinnedShortcuts_multi() {
         // Create some shortcuts.
         runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
             assertTrue(mManager.setDynamicShortcuts(list(
@@ -2831,7 +2831,7 @@
         });
     }
 
-    public void testPinShortcutAndGetPinnedShortcuts_assistant() {
+    public void PinShortcutAndGetPinnedShortcuts_assistant() {
         // Create some shortcuts.
         runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
             assertTrue(mManager.setDynamicShortcuts(list(
@@ -2887,7 +2887,7 @@
         });
     }
 
-    public void testPinShortcutAndGetPinnedShortcuts_crossProfile_plusLaunch() {
+    public void PinShortcutAndGetPinnedShortcuts_crossProfile_plusLaunch() {
         // Create some shortcuts.
         runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
             assertTrue(mManager.setDynamicShortcuts(list(
@@ -3476,7 +3476,7 @@
         });
     }
 
-    public void testStartShortcut() {
+    public void StartShortcut() {
         // Create some shortcuts.
         runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
             final ShortcutInfo s1_1 = makeShortcut(
@@ -3611,7 +3611,7 @@
         // TODO Check extra, etc
     }
 
-    public void testLauncherCallback() throws Throwable {
+    public void LauncherCallback() throws Throwable {
         // Disable throttling for this test.
         mService.updateConfigurationLocked(
                 ConfigConstants.KEY_MAX_UPDATES_PER_INTERVAL + "=99999999,"
@@ -3777,7 +3777,7 @@
                 .isEmpty();
     }
 
-    public void testLauncherCallback_crossProfile() throws Throwable {
+    public void LauncherCallback_crossProfile() throws Throwable {
         prepareCrossProfileDataSet();
 
         final Handler h = new Handler(Looper.getMainLooper());
@@ -3900,7 +3900,7 @@
 
     // === Test for persisting ===
 
-    public void testSaveAndLoadUser_empty() {
+    public void SaveAndLoadUser_empty() {
         assertTrue(mManager.setDynamicShortcuts(list()));
 
         Log.i(TAG, "Saved state");
@@ -3917,7 +3917,7 @@
     /**
      * Try save and load, also stop/start the user.
      */
-    public void testSaveAndLoadUser() {
+    public void SaveAndLoadUser() {
         // First, create some shortcuts and save.
         runWithCaller(CALLING_PACKAGE_1, UserHandle.USER_SYSTEM, () -> {
             final Icon icon1 = Icon.createWithResource(getTestContext(), R.drawable.black_64x16);
@@ -4058,7 +4058,7 @@
         // TODO Check all other fields
     }
 
-    public void testLoadCorruptedShortcuts() throws Exception {
+    public void LoadCorruptedShortcuts() throws Exception {
         initService();
 
         addPackage("com.android.chrome", 0, 0);
@@ -4072,7 +4072,7 @@
         assertNull(ShortcutPackage.loadFromFile(mService, user, corruptedShortcutPackage, false));
     }
 
-    public void testSaveCorruptAndLoadUser() throws Exception {
+    public void SaveCorruptAndLoadUser() throws Exception {
         // First, create some shortcuts and save.
         runWithCaller(CALLING_PACKAGE_1, UserHandle.USER_SYSTEM, () -> {
             final Icon icon1 = Icon.createWithResource(getTestContext(), R.drawable.black_64x16);
@@ -4228,7 +4228,7 @@
         // TODO Check all other fields
     }
 
-    public void testCleanupPackage() {
+    public void CleanupPackage() {
         runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
             assertTrue(mManager.setDynamicShortcuts(list(
                     makeShortcut("s0_1"))));
@@ -4505,7 +4505,7 @@
         mService.saveDirtyInfo();
     }
 
-    public void testCleanupPackage_republishManifests() {
+    public void CleanupPackage_republishManifests() {
         addManifestShortcutResource(
                 new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
                 R.xml.shortcut_2);
@@ -4573,7 +4573,7 @@
         });
     }
 
-    public void testHandleGonePackage_crossProfile() {
+    public void HandleGonePackage_crossProfile() {
         // Create some shortcuts.
         runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
             assertTrue(mManager.setDynamicShortcuts(list(
@@ -4845,7 +4845,7 @@
         assertEquals(expected, spi.canRestoreTo(mService, pi, true));
     }
 
-    public void testCanRestoreTo() {
+    public void CanRestoreTo() {
         addPackage(CALLING_PACKAGE_1, CALLING_UID_1, 10, "sig1");
         addPackage(CALLING_PACKAGE_2, CALLING_UID_2, 10, "sig1", "sig2");
         addPackage(CALLING_PACKAGE_3, CALLING_UID_3, 10, "sig1");
@@ -4908,7 +4908,7 @@
         checkCanRestoreTo(DISABLED_REASON_BACKUP_NOT_SUPPORTED, spi3, true, 10, true, "sig1");
     }
 
-    public void testHandlePackageDelete() {
+    public void HandlePackageDelete() {
         checkHandlePackageDeleteInner((userId, packageName) -> {
             uninstallPackage(userId, packageName);
             mService.mPackageMonitor.onReceive(getTestContext(),
@@ -4916,7 +4916,7 @@
         });
     }
 
-    public void testHandlePackageDisable() {
+    public void HandlePackageDisable() {
         checkHandlePackageDeleteInner((userId, packageName) -> {
             disablePackage(userId, packageName);
             mService.mPackageMonitor.onReceive(getTestContext(),
@@ -5048,7 +5048,7 @@
     }
 
     /** Almost ame as testHandlePackageDelete, except it doesn't uninstall packages. */
-    public void testHandlePackageClearData() {
+    public void HandlePackageClearData() {
         final Icon bmp32x32 = Icon.createWithBitmap(BitmapFactory.decodeResource(
                 getTestContext().getResources(), R.drawable.black_32x32));
         setCaller(CALLING_PACKAGE_1, USER_0);
@@ -5124,7 +5124,7 @@
         assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_3, USER_10));
     }
 
-    public void testHandlePackageClearData_manifestRepublished() {
+    public void HandlePackageClearData_manifestRepublished() {
 
         mRunningUsers.put(USER_10, true);
 
@@ -5166,7 +5166,7 @@
         });
     }
 
-    public void testHandlePackageUpdate() throws Throwable {
+    public void HandlePackageUpdate() throws Throwable {
         // Set up shortcuts and launchers.
 
         final Icon res32x32 = Icon.createWithResource(getTestContext(), R.drawable.black_32x32);
@@ -5340,7 +5340,7 @@
     /**
      * Test the case where an updated app has resource IDs changed.
      */
-    public void testHandlePackageUpdate_resIdChanged() throws Exception {
+    public void HandlePackageUpdate_resIdChanged() throws Exception {
         final Icon icon1 = Icon.createWithResource(getTestContext(), /* res ID */ 1000);
         final Icon icon2 = Icon.createWithResource(getTestContext(), /* res ID */ 1001);
 
@@ -5415,7 +5415,7 @@
         });
     }
 
-    public void testHandlePackageUpdate_systemAppUpdate() {
+    public void HandlePackageUpdate_systemAppUpdate() {
 
         // Package1 is a system app.  Package 2 is not a system app, so it's not scanned
         // in this test at all.
@@ -5521,7 +5521,7 @@
                 mService.getUserShortcutsLocked(USER_0).getLastAppScanOsFingerprint());
     }
 
-    public void testHandlePackageChanged() {
+    public void HandlePackageChanged() {
         final ComponentName ACTIVITY1 = new ComponentName(CALLING_PACKAGE_1, "act1");
         final ComponentName ACTIVITY2 = new ComponentName(CALLING_PACKAGE_1, "act2");
 
@@ -5651,7 +5651,7 @@
         });
     }
 
-    public void testHandlePackageUpdate_activityNoLongerMain() throws Throwable {
+    public void HandlePackageUpdate_activityNoLongerMain() throws Throwable {
         runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
             assertTrue(mManager.setDynamicShortcuts(list(
                     makeShortcutWithActivity("s1a",
@@ -5737,7 +5737,7 @@
      * - Unpinned dynamic shortcuts
      * - Bitmaps
      */
-    public void testBackupAndRestore() {
+    public void BackupAndRestore() {
 
         assertFileNotExists("user-0/shortcut_dump/restore-0-start.txt");
         assertFileNotExists("user-0/shortcut_dump/restore-1-payload.xml");
@@ -5758,7 +5758,7 @@
         checkBackupAndRestore_success(/*firstRestore=*/ true);
     }
 
-    public void testBackupAndRestore_backupRestoreTwice() {
+    public void BackupAndRestore_backupRestoreTwice() {
         prepareForBackupTest();
 
         checkBackupAndRestore_success(/*firstRestore=*/ true);
@@ -5774,7 +5774,7 @@
         checkBackupAndRestore_success(/*firstRestore=*/ false);
     }
 
-    public void testBackupAndRestore_restoreToNewVersion() {
+    public void BackupAndRestore_restoreToNewVersion() {
         prepareForBackupTest();
 
         addPackage(CALLING_PACKAGE_1, CALLING_UID_1, 2);
@@ -5783,7 +5783,7 @@
         checkBackupAndRestore_success(/*firstRestore=*/ true);
     }
 
-    public void testBackupAndRestore_restoreToSuperSetSignatures() {
+    public void BackupAndRestore_restoreToSuperSetSignatures() {
         prepareForBackupTest();
 
         // Change package signatures.
@@ -5980,7 +5980,7 @@
         });
     }
 
-    public void testBackupAndRestore_publisherWrongSignature() {
+    public void BackupAndRestore_publisherWrongSignature() {
         prepareForBackupTest();
 
         addPackage(CALLING_PACKAGE_1, CALLING_UID_1, 10, "sigx"); // different signature
@@ -5988,7 +5988,7 @@
         checkBackupAndRestore_publisherNotRestored(ShortcutInfo.DISABLED_REASON_SIGNATURE_MISMATCH);
     }
 
-    public void testBackupAndRestore_publisherNoLongerBackupTarget() {
+    public void BackupAndRestore_publisherNoLongerBackupTarget() {
         prepareForBackupTest();
 
         updatePackageInfo(CALLING_PACKAGE_1,
@@ -6117,7 +6117,7 @@
         });
     }
 
-    public void testBackupAndRestore_launcherLowerVersion() {
+    public void BackupAndRestore_launcherLowerVersion() {
         prepareForBackupTest();
 
         addPackage(LAUNCHER_1, LAUNCHER_UID_1, 0); // Lower version
@@ -6126,7 +6126,7 @@
         checkBackupAndRestore_success(/*firstRestore=*/ true);
     }
 
-    public void testBackupAndRestore_launcherWrongSignature() {
+    public void BackupAndRestore_launcherWrongSignature() {
         prepareForBackupTest();
 
         addPackage(LAUNCHER_1, LAUNCHER_UID_1, 10, "sigx"); // different signature
@@ -6134,7 +6134,7 @@
         checkBackupAndRestore_launcherNotRestored(true);
     }
 
-    public void testBackupAndRestore_launcherNoLongerBackupTarget() {
+    public void BackupAndRestore_launcherNoLongerBackupTarget() {
         prepareForBackupTest();
 
         updatePackageInfo(LAUNCHER_1,
@@ -6239,7 +6239,7 @@
         });
     }
 
-    public void testBackupAndRestore_launcherAndPackageNoLongerBackupTarget() {
+    public void BackupAndRestore_launcherAndPackageNoLongerBackupTarget() {
         prepareForBackupTest();
 
         updatePackageInfo(CALLING_PACKAGE_1,
@@ -6337,7 +6337,7 @@
         });
     }
 
-    public void testBackupAndRestore_disabled() {
+    public void BackupAndRestore_disabled() {
         prepareCrossProfileDataSet();
 
         // Before doing backup & restore, disable s1.
@@ -6402,7 +6402,7 @@
     }
 
 
-    public void testBackupAndRestore_manifestRePublished() {
+    public void BackupAndRestore_manifestRePublished() {
         // Publish two manifest shortcuts.
         addManifestShortcutResource(
                 new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
@@ -6493,7 +6493,7 @@
      * logcat.
      * - if it has allowBackup=false, we don't touch any of the existing shortcuts.
      */
-    public void testBackupAndRestore_appAlreadyInstalledWhenRestored() {
+    public void BackupAndRestore_appAlreadyInstalledWhenRestored() {
         // Pre-backup.  Same as testBackupAndRestore_manifestRePublished().
 
         // Publish two manifest shortcuts.
@@ -6618,7 +6618,7 @@
     /**
      * Test for restoring the pre-P backup format.
      */
-    public void testBackupAndRestore_api27format() throws Exception {
+    public void BackupAndRestore_api27format() throws Exception {
         final byte[] payload = readTestAsset("shortcut/shortcut_api27_backup.xml").getBytes();
 
         addPackage(CALLING_PACKAGE_1, CALLING_UID_1, 10, "22222");
@@ -6656,7 +6656,7 @@
 
     }
 
-    public void testSaveAndLoad_crossProfile() {
+    public void SaveAndLoad_crossProfile() {
         prepareCrossProfileDataSet();
 
         dumpsysOnLogcat("Before save & load");
@@ -6859,7 +6859,7 @@
                         .getPackageUserId());
     }
 
-    public void testOnApplicationActive_permission() {
+    public void OnApplicationActive_permission() {
         assertExpectException(SecurityException.class, "Missing permission", () ->
                 mManager.onApplicationActive(CALLING_PACKAGE_1, USER_0));
 
@@ -6868,7 +6868,7 @@
         mManager.onApplicationActive(CALLING_PACKAGE_1, USER_0);
     }
 
-    public void testGetShareTargets_permission() {
+    public void GetShareTargets_permission() {
         addPackage(CHOOSER_ACTIVITY_PACKAGE, CHOOSER_ACTIVITY_UID, 10, "sig1");
         mInjectedChooserActivity =
                 ComponentName.createRelative(CHOOSER_ACTIVITY_PACKAGE, ".ChooserActivity");
@@ -6887,7 +6887,7 @@
         });
     }
 
-    public void testHasShareTargets_permission() {
+    public void HasShareTargets_permission() {
         assertExpectException(SecurityException.class, "Missing permission", () ->
                 mManager.hasShareTargets(CALLING_PACKAGE_1));
 
@@ -6896,7 +6896,7 @@
         mManager.hasShareTargets(CALLING_PACKAGE_1);
     }
 
-    public void testisSharingShortcut_permission() throws IntentFilter.MalformedMimeTypeException {
+    public void isSharingShortcut_permission() throws IntentFilter.MalformedMimeTypeException {
         setCaller(LAUNCHER_1, USER_0);
 
         IntentFilter filter_any = new IntentFilter();
@@ -6911,18 +6911,18 @@
         mManager.hasShareTargets(CALLING_PACKAGE_1);
     }
 
-    public void testDumpsys_crossProfile() {
+    public void Dumpsys_crossProfile() {
         prepareCrossProfileDataSet();
         dumpsysOnLogcat("test1", /* force= */ true);
     }
 
-    public void testDumpsys_withIcons() throws IOException {
-        testIcons();
+    public void Dumpsys_withIcons() throws IOException {
+        Icons();
         // Dump after having some icons.
         dumpsysOnLogcat("test1", /* force= */ true);
     }
 
-    public void testManifestShortcut_publishOnUnlockUser() {
+    public void ManifestShortcut_publishOnUnlockUser() {
         addManifestShortcutResource(
                 new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
                 R.xml.shortcut_1);
@@ -7136,7 +7136,7 @@
         assertNull(mService.getPackageShortcutForTest(LAUNCHER_1, USER_0));
     }
 
-    public void testManifestShortcut_publishOnBroadcast() {
+    public void ManifestShortcut_publishOnBroadcast() {
         // First, no packages are installed.
         uninstallPackage(USER_0, CALLING_PACKAGE_1);
         uninstallPackage(USER_0, CALLING_PACKAGE_2);
@@ -7392,7 +7392,7 @@
         });
     }
 
-    public void testManifestShortcuts_missingMandatoryFields() {
+    public void ManifestShortcuts_missingMandatoryFields() {
         // Start with no apps installed.
         uninstallPackage(USER_0, CALLING_PACKAGE_1);
         uninstallPackage(USER_0, CALLING_PACKAGE_2);
@@ -7461,7 +7461,7 @@
         });
     }
 
-    public void testManifestShortcuts_intentDefinitions() {
+    public void ManifestShortcuts_intentDefinitions() {
         addManifestShortcutResource(
                 new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
                 R.xml.shortcut_error_4);
@@ -7603,7 +7603,7 @@
         });
     }
 
-    public void testManifestShortcuts_checkAllFields() {
+    public void ManifestShortcuts_checkAllFields() {
         mService.handleUnlockUser(USER_0);
 
         // Package 1 updated, which has one valid manifest shortcut and one invalid.
@@ -7708,7 +7708,7 @@
         });
     }
 
-    public void testManifestShortcuts_localeChange() throws InterruptedException {
+    public void ManifestShortcuts_localeChange() throws InterruptedException {
         mService.handleUnlockUser(USER_0);
 
         // Package 1 updated, which has one valid manifest shortcut and one invalid.
@@ -7812,7 +7812,7 @@
         });
     }
 
-    public void testManifestShortcuts_updateAndDisabled_notPinned() {
+    public void ManifestShortcuts_updateAndDisabled_notPinned() {
         mService.handleUnlockUser(USER_0);
 
         // First, just publish a manifest shortcut.
@@ -7852,7 +7852,7 @@
         });
     }
 
-    public void testManifestShortcuts_updateAndDisabled_pinned() {
+    public void ManifestShortcuts_updateAndDisabled_pinned() {
         mService.handleUnlockUser(USER_0);
 
         // First, just publish a manifest shortcut.
@@ -7908,7 +7908,7 @@
         });
     }
 
-    public void testManifestShortcuts_duplicateInSingleActivity() {
+    public void ManifestShortcuts_duplicateInSingleActivity() {
         mService.handleUnlockUser(USER_0);
 
         // The XML has two shortcuts with the same ID.
@@ -7933,7 +7933,7 @@
         });
     }
 
-    public void testManifestShortcuts_duplicateInTwoActivities() {
+    public void ManifestShortcuts_duplicateInTwoActivities() {
         mService.handleUnlockUser(USER_0);
 
         // ShortcutActivity has shortcut ms1
@@ -7985,7 +7985,7 @@
     /**
      * Manifest shortcuts cannot override shortcuts that were published via the APIs.
      */
-    public void testManifestShortcuts_cannotOverrideNonManifest() {
+    public void ManifestShortcuts_cannotOverrideNonManifest() {
         mService.handleUnlockUser(USER_0);
 
         // Create a non-pinned dynamic shortcut and a non-dynamic pinned shortcut.
@@ -8058,7 +8058,7 @@
     /**
      * Make sure the APIs won't work on manifest shortcuts.
      */
-    public void testManifestShortcuts_immutable() {
+    public void ManifestShortcuts_immutable() {
         mService.handleUnlockUser(USER_0);
 
         // Create a non-pinned manifest shortcut, a pinned shortcut that was originally
@@ -8151,7 +8151,7 @@
     /**
      * Make sure the APIs won't work on manifest shortcuts.
      */
-    public void testManifestShortcuts_tooMany() {
+    public void ManifestShortcuts_tooMany() {
         // Change the max number of shortcuts.
         mService.updateConfigurationLocked(ConfigConstants.KEY_MAX_SHORTCUTS + "=3");
 
@@ -8170,7 +8170,7 @@
         });
     }
 
-    public void testMaxShortcutCount_set() {
+    public void MaxShortcutCount_set() {
         // Change the max number of shortcuts.
         mService.updateConfigurationLocked(ConfigConstants.KEY_MAX_SHORTCUTS + "=3");
 
@@ -8251,7 +8251,7 @@
         });
     }
 
-    public void testMaxShortcutCount_add() {
+    public void MaxShortcutCount_add() {
         // Change the max number of shortcuts.
         mService.updateConfigurationLocked(ConfigConstants.KEY_MAX_SHORTCUTS + "=3");
 
@@ -8378,7 +8378,7 @@
         });
     }
 
-    public void testMaxShortcutCount_update() {
+    public void MaxShortcutCount_update() {
         // Change the max number of shortcuts.
         mService.updateConfigurationLocked(ConfigConstants.KEY_MAX_SHORTCUTS + "=3");
 
@@ -8469,7 +8469,7 @@
         });
     }
 
-    public void testShortcutsPushedOutByManifest() {
+    public void ShortcutsPushedOutByManifest() {
         // Change the max number of shortcuts.
         mService.updateConfigurationLocked(ConfigConstants.KEY_MAX_SHORTCUTS + "=3");
 
@@ -8577,7 +8577,7 @@
         });
     }
 
-    public void testReturnedByServer() {
+    public void ReturnedByServer() {
         // Package 1 updated, with manifest shortcuts.
         addManifestShortcutResource(
                 new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
@@ -8623,7 +8623,7 @@
         });
     }
 
-    public void testIsForegroundDefaultLauncher_true() {
+    public void IsForegroundDefaultLauncher_true() {
         final int uid = 1024;
 
         setDefaultLauncher(UserHandle.USER_SYSTEM, "default");
@@ -8633,7 +8633,7 @@
     }
 
 
-    public void testIsForegroundDefaultLauncher_defaultButNotForeground() {
+    public void IsForegroundDefaultLauncher_defaultButNotForeground() {
         final int uid = 1024;
 
         setDefaultLauncher(UserHandle.USER_SYSTEM, "default");
@@ -8642,7 +8642,7 @@
         assertFalse(mInternal.isForegroundDefaultLauncher("default", uid));
     }
 
-    public void testIsForegroundDefaultLauncher_foregroundButNotDefault() {
+    public void IsForegroundDefaultLauncher_foregroundButNotDefault() {
         final int uid = 1024;
 
         setDefaultLauncher(UserHandle.USER_SYSTEM, "default");
@@ -8651,7 +8651,7 @@
         assertFalse(mInternal.isForegroundDefaultLauncher("another", uid));
     }
 
-    public void testParseShareTargetsFromManifest() {
+    public void ParseShareTargetsFromManifest() {
         // These values must exactly match the content of shortcuts_share_targets.xml resource
         List<ShareTargetInfo> expectedValues = new ArrayList<>();
         expectedValues.add(new ShareTargetInfo(
@@ -8703,7 +8703,7 @@
         }
     }
 
-    public void testShareTargetInfo_saveToXml() throws IOException, XmlPullParserException {
+    public void ShareTargetInfo_saveToXml() throws IOException, XmlPullParserException {
         List<ShareTargetInfo> expectedValues = new ArrayList<>();
         expectedValues.add(new ShareTargetInfo(
                 new ShareTargetInfo.TargetData[]{new ShareTargetInfo.TargetData(
@@ -8769,7 +8769,7 @@
         }
     }
 
-    public void testIsSharingShortcut() throws IntentFilter.MalformedMimeTypeException {
+    public void IsSharingShortcut() throws IntentFilter.MalformedMimeTypeException {
         addManifestShortcutResource(
                 new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
                 R.xml.shortcut_share_targets);
@@ -8819,7 +8819,7 @@
                 filter_any));
     }
 
-    public void testIsSharingShortcut_PinnedAndCachedOnlyShortcuts()
+    public void IsSharingShortcut_PinnedAndCachedOnlyShortcuts()
             throws IntentFilter.MalformedMimeTypeException {
         addManifestShortcutResource(
                 new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
@@ -8876,7 +8876,7 @@
                 filter_any));
     }
 
-    public void testAddingShortcuts_ExcludesHiddenFromLauncherShortcuts() {
+    public void AddingShortcuts_ExcludesHiddenFromLauncherShortcuts() {
         final ShortcutInfo s1 = makeShortcutExcludedFromLauncher("s1");
         final ShortcutInfo s2 = makeShortcutExcludedFromLauncher("s2");
         final ShortcutInfo s3 = makeShortcutExcludedFromLauncher("s3");
@@ -8897,7 +8897,7 @@
         });
     }
 
-    public void testUpdateShortcuts_ExcludesHiddenFromLauncherShortcuts() {
+    public void UpdateShortcuts_ExcludesHiddenFromLauncherShortcuts() {
         final ShortcutInfo s1 = makeShortcut("s1");
         final ShortcutInfo s2 = makeShortcut("s2");
         final ShortcutInfo s3 = makeShortcut("s3");
@@ -8910,7 +8910,7 @@
         });
     }
 
-    public void testPinHiddenShortcuts_ThrowsException() {
+    public void PinHiddenShortcuts_ThrowsException() {
         runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
             assertThrown(IllegalArgumentException.class, () -> {
                 mManager.requestPinShortcut(makeShortcutExcludedFromLauncher("s1"), null);
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerCacheTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerCacheTest.java
index d69e476..9b878b3 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerCacheTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerCacheTest.java
@@ -19,6 +19,7 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.junit.Assume.assumeTrue;
+import static org.junit.Assert.fail;
 
 import android.app.ActivityManager;
 import android.app.LocaleManager;
@@ -26,6 +27,7 @@
 import android.content.pm.PackageManager;
 import android.content.pm.UserInfo;
 import android.multiuser.Flags;
+import android.os.Bundle;
 import android.os.LocaleList;
 import android.os.SystemClock;
 import android.os.UserHandle;
@@ -261,6 +263,162 @@
         assertThat(um.getUserName()).isEqualTo(newName);
     }
 
+
+    @MediumTest
+    @Test
+    public void testDefaultRestrictionsApplied() throws Exception {
+        final UserInfo userInfo = mUserManager.createUser("Useroid",
+                UserManager.USER_TYPE_FULL_SECONDARY, 0);
+        mUsersToRemove.add(userInfo.id);
+        final UserTypeDetails userTypeDetails =
+                UserTypeFactory.getUserTypes().get(UserManager.USER_TYPE_FULL_SECONDARY);
+        final Bundle expectedRestrictions = userTypeDetails.getDefaultRestrictions();
+        // Note this can fail if DO unset those restrictions.
+        for (String restriction : expectedRestrictions.keySet()) {
+            if (expectedRestrictions.getBoolean(restriction)) {
+                assertThat(mUserManager.hasUserRestriction(restriction, UserHandle.of(userInfo.id)))
+                        .isTrue();
+                // Test cached value
+                assertThat(mUserManager.hasUserRestriction(restriction, UserHandle.of(userInfo.id)))
+                        .isTrue();
+            }
+        }
+    }
+
+    @MediumTest
+    @Test
+    public void testSetDefaultGuestRestrictions() {
+        final Bundle origRestrictions = mUserManager.getDefaultGuestRestrictions();
+        try {
+            final boolean isFunDisallowed = origRestrictions.getBoolean(UserManager.DISALLOW_FUN,
+                    false);
+            final UserInfo guest1 = mUserManager.createUser("Guest 1", UserInfo.FLAG_GUEST);
+            assertThat(guest1).isNotNull();
+            assertThat(mUserManager.hasUserRestriction(UserManager.DISALLOW_FUN,
+                    guest1.getUserHandle())).isEqualTo(isFunDisallowed);
+            removeUser(guest1.id, true);
+            // Cache return false after user was removed
+            assertThat(mUserManager.hasUserRestriction(UserManager.DISALLOW_FUN,
+                    guest1.getUserHandle())).isFalse();
+
+            Bundle restrictions = new Bundle();
+            restrictions.putBoolean(UserManager.DISALLOW_FUN, !isFunDisallowed);
+            mUserManager.setDefaultGuestRestrictions(restrictions);
+            UserInfo guest2 = mUserManager.createUser("Guest 2", UserInfo.FLAG_GUEST);
+            assertThat(guest2).isNotNull();
+            assertThat(mUserManager.hasUserRestriction(UserManager.DISALLOW_FUN,
+                    guest2.getUserHandle())).isNotEqualTo(isFunDisallowed);
+            removeUser(guest2.id, true);
+            assertThat(mUserManager.getUserInfo(guest2.id)).isNull();
+            assertThat(mUserManager.hasUserRestriction(UserManager.DISALLOW_FUN,
+                    guest2.getUserHandle())).isFalse();
+        } finally {
+            mUserManager.setDefaultGuestRestrictions(origRestrictions);
+        }
+    }
+
+    @MediumTest
+    @Test
+    public void testCacheInvalidatedAfterUserAddedOrRemoved() {
+        final Bundle origRestrictions = mUserManager.getDefaultGuestRestrictions();
+        try {
+            final boolean isFunDisallowed = origRestrictions.getBoolean(UserManager.DISALLOW_FUN,
+                    false);
+            final UserInfo guest1 = mUserManager.createUser("Guest 1", UserInfo.FLAG_GUEST);
+            assertThat(guest1).isNotNull();
+            assertThat(mUserManager.hasUserRestriction(UserManager.DISALLOW_FUN,
+                    guest1.getUserHandle())).isEqualTo(isFunDisallowed);
+            removeUser(guest1.id, true);
+
+            Bundle restrictions = new Bundle();
+            restrictions.putBoolean(UserManager.DISALLOW_FUN, !isFunDisallowed);
+            mUserManager.setDefaultGuestRestrictions(restrictions);
+            int latest_id = guest1.id;
+            // Cache removed id and few next ids.
+            assertThat(mUserManager.hasUserRestriction(UserManager.DISALLOW_FUN,
+                    UserHandle.of(latest_id))).isFalse();
+            assertThat(mUserManager.hasUserRestriction(UserManager.DISALLOW_FUN,
+                    UserHandle.of(latest_id + 1))).isFalse();
+            assertThat(mUserManager.hasUserRestriction(UserManager.DISALLOW_FUN,
+                    UserHandle.of(latest_id + 2))).isFalse();
+            assertThat(mUserManager.hasUserRestriction(UserManager.DISALLOW_FUN,
+                    UserHandle.of(latest_id + 3))).isFalse();
+
+            UserInfo guest2 = mUserManager.createUser("Guest 2", UserInfo.FLAG_GUEST);
+            assertThat(guest2).isNotNull();
+            // Cache was invalidated after user was added
+            assertThat(mUserManager.hasUserRestriction(UserManager.DISALLOW_FUN,
+                    guest2.getUserHandle())).isTrue();
+            removeUser(guest2.id, true);
+            assertThat(mUserManager.getUserInfo(guest2.id)).isNull();
+            // Cache was invalidated after user was removed
+            assertThat(mUserManager.hasUserRestriction(UserManager.DISALLOW_FUN,
+                    guest2.getUserHandle())).isFalse();
+        } finally {
+            mUserManager.setDefaultGuestRestrictions(origRestrictions);
+        }
+    }
+
+
+    @MediumTest
+    @Test
+    public void testAddRemoveUsersAndRestrictions() {
+        try {
+            final UserInfo userInfo = mUserManager.createUser("Useroid",
+                    UserManager.USER_TYPE_FULL_SECONDARY, 0);
+            mUsersToRemove.add(userInfo.id);
+            assertThat(mUserManager.hasUserRestriction(UserManager.DISALLOW_FUN,
+                    userInfo.getUserHandle())).isFalse();
+            mUserManager.setUserRestriction(UserManager.DISALLOW_FUN, true,
+                    userInfo.getUserHandle());
+
+            assertThat(mUserManager.hasUserRestriction(UserManager.DISALLOW_FUN,
+                    userInfo.getUserHandle())).isTrue();
+            removeUser(userInfo.id, true);
+            assertThat(mUserManager.getUserSerialNumber(userInfo.id)).isEqualTo(-1);
+            assertThat(mUserManager.getUserInfo(userInfo.id)).isNull();
+            assertThat(mUserManager.hasUserRestriction(UserManager.DISALLOW_FUN,
+                    userInfo.getUserHandle())).isFalse();
+        } catch (java.lang.Exception e) {
+        }
+    }
+
+
+    private void sleep(long millis) {
+        try {
+            Thread.sleep(millis);
+        } catch (InterruptedException e) {
+            e.printStackTrace();
+        }
+    }
+
+
+    @MediumTest
+    @Test
+    public void testDefaultUserRestrictionsForPrivateProfile() {
+        assumeTrue(mUserManager.canAddPrivateProfile());
+        final int currentUserId = ActivityManager.getCurrentUser();
+        UserInfo privateProfileInfo = null;
+        try {
+            privateProfileInfo = mUserManager.createProfileForUser(
+                    "Private", UserManager.USER_TYPE_PROFILE_PRIVATE, 0, currentUserId, null);
+            assertThat(privateProfileInfo).isNotNull();
+        } catch (Exception e) {
+            fail("Creation of private profile failed due to " + e.getMessage());
+        }
+        assertDefaultPrivateProfileRestrictions(privateProfileInfo.getUserHandle());
+        // Assert cached values
+        assertDefaultPrivateProfileRestrictions(privateProfileInfo.getUserHandle());
+    }
+
+    private void assertDefaultPrivateProfileRestrictions(UserHandle userHandle) {
+        Bundle defaultPrivateProfileRestrictions =
+                UserTypeFactory.getDefaultPrivateProfileRestrictions();
+        for (String restriction : defaultPrivateProfileRestrictions.keySet()) {
+            assertThat(mUserManager.hasUserRestrictionForUser(restriction, userHandle)).isTrue();
+        }
+    }
+
     private void assumeManagedUsersSupported() {
         // In Automotive, if headless system user is enabled, a managed user cannot be created
         // under a primary user.
@@ -270,9 +428,23 @@
     }
 
     private void removeUser(int userId) {
+        removeUser(userId, false);
+    }
+
+    private void removeUser(int userId, boolean waitForCompleteRemoval) {
         mUserManager.removeUser(userId);
         mUserRemovalWaiter.waitFor(userId);
         mUsersToRemove.remove(userId);
+        if (waitForCompleteRemoval) {
+            int serialNumber = mUserManager.getUserSerialNumber(userId);
+            int timeout = REMOVE_USER_TIMEOUT_SECONDS * 5; // called every 200ms
+            // Wait for the user to be removed from memory
+            while (serialNumber > 0 && timeout > 0) {
+                sleep(200);
+                timeout--;
+                serialNumber = mUserManager.getUserSerialNumber(userId);
+            }
+        }
     }
 
     private boolean isAutomotive() {
diff --git a/services/tests/servicestests/src/com/android/server/utils/LazyJniRegistrarTest.java b/services/tests/servicestests/src/com/android/server/utils/LazyJniRegistrarTest.java
new file mode 100644
index 0000000..a2df73b
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/utils/LazyJniRegistrarTest.java
@@ -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.server.utils;
+
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
+@SmallTest
+@Presubmit
+@RunWith(JUnit4.class)
+public class LazyJniRegistrarTest {
+
+    @Test
+    public void testNativeMethodsResolve() throws Exception {
+        // Basic test with a few explicit invocations to make sure methods resolve and don't throw.
+        LazyJniRegistrar.registerConsumerIrService();
+        LazyJniRegistrar.registerGameManagerService();
+        LazyJniRegistrar.registerVrManagerService();
+    }
+
+    @Test
+    public void testAllNativeRegisterMethodsResolve() throws Exception {
+        // Catch-all test to make sure public static register* methods resolve and don't throw.
+        for (Method method : LazyJniRegistrar.class.getDeclaredMethods()) {
+            if (Modifier.isPublic(method.getModifiers())
+                    && Modifier.isStatic(method.getModifiers())
+                    && method.getName().startsWith("register")) {
+                method.invoke(null);
+            }
+        }
+    }
+
+    // TODO(b/302724778): Remove manual JNI load
+    static {
+        System.loadLibrary("servicestestjni");
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/utils/OWNERS b/services/tests/servicestests/src/com/android/server/utils/OWNERS
index f5b19a1..69b9fa2 100644
--- a/services/tests/servicestests/src/com/android/server/utils/OWNERS
+++ b/services/tests/servicestests/src/com/android/server/utils/OWNERS
@@ -1,5 +1,6 @@
 per-file EventLoggerTest.java = file:/platform/frameworks/av:/media/janitors/media_solutions_OWNERS
 per-file EventLoggerTest.java = jmtrivi@google.com
+per-file LazyJniRegistrarTest.java = file:/PERFORMANCE_OWNERS
 
 # Bug component : 158088 = per-file AnrTimer*.java
 per-file AnrTimer*.java = file:/PERFORMANCE_OWNERS
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/EventConditionProviderTest.java b/services/tests/uiservicestests/src/com/android/server/notification/EventConditionProviderTest.java
index fa1372d..87b9154 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/EventConditionProviderTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/EventConditionProviderTest.java
@@ -88,6 +88,8 @@
         mService.mContext = this.getContext();
 
         mContext.addMockSystemService(UserManager.class, mUserManager);
+        when(mUserManager.getProfiles(eq(UserHandle.USER_SYSTEM))).thenReturn(
+                List.of(new UserInfo(UserHandle.USER_SYSTEM, "USER_SYSTEM", 0)));
         when(mUserManager.getProfiles(eq(mUserId))).thenReturn(
                 List.of(new UserInfo(mUserId, "mUserId", 0)));
 
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 074cbb5..20f4bb6 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -311,6 +311,7 @@
 
 import com.android.internal.R;
 import com.android.internal.config.sysui.TestableFlagResolver;
+import com.android.internal.logging.InstanceId;
 import com.android.internal.logging.InstanceIdSequence;
 import com.android.internal.logging.InstanceIdSequenceFake;
 import com.android.internal.messages.nano.SystemMessageProto;
@@ -7637,7 +7638,8 @@
                 mTestNotificationChannel, 1, null, true));
         when(r1.getLifespanMs(anyLong())).thenReturn(234);
 
-        r1.getSbn().setInstanceId(mNotificationInstanceIdSequence.newInstanceId());
+        InstanceId instanceId1 = mNotificationInstanceIdSequence.newInstanceId();
+        r1.getSbn().setInstanceId(instanceId1);
         // Enqueues the notification to be posted, so hasPosted will be false.
         mService.addEnqueuedNotification(r1);
 
@@ -7648,8 +7650,10 @@
                 r1.getSbn().getPackageName(), r1.getKey(), signals, "",
                 r1.getUser().getIdentifier());
         mBinderService.applyEnqueuedAdjustmentFromAssistant(null, adjustment1);
-        assertTrue(mService.checkLastClassificationChannelLog(false /*hasPosted*/,
-                true /*isAlerting*/, 3 /*TYPE_NEWS*/, 234));
+        assertTrue(mService.checkLastClassificationChannelLog(false /*=hasPosted*/,
+                true /*=isAlerting*/, Adjustment.TYPE_NEWS, 234,
+                NOTIFICATION_ADJUSTED.getId(),
+                instanceId1.getId(), r1.getUid()));
 
         // Set up notifications that will be adjusted
         // This notification starts on a low importance channel, so isAlerting is false.
@@ -7659,7 +7663,8 @@
                 mLowImportanceNotificationChannel, 1, null, true));
         when(r2.getLifespanMs(anyLong())).thenReturn(345);
 
-        r2.getSbn().setInstanceId(mNotificationInstanceIdSequence.newInstanceId());
+        InstanceId instanceId2 = mNotificationInstanceIdSequence.newInstanceId();
+        r2.getSbn().setInstanceId(instanceId2);
         // Adds the notification as already posted, so hasPosted will be true.
         mService.addNotification(r2);
         // The signal is removed when used so it has to be readded.
@@ -7669,15 +7674,19 @@
                 r2.getUser().getIdentifier());
         mBinderService.applyEnqueuedAdjustmentFromAssistant(null, adjustment2);
         assertTrue(mService.checkLastClassificationChannelLog(true /*hasPosted*/,
-                false /*isAlerting*/, 3 /*TYPE_NEWS*/, 345)); // currently failing
+                false /*isAlerting*/, Adjustment.TYPE_NEWS, 345,
+                NOTIFICATION_ADJUSTED.getId(),
+                instanceId2.getId() /*instance_id*/, r2.getUid()));
 
         signals.putInt(Adjustment.KEY_TYPE, Adjustment.TYPE_PROMOTION);
         Adjustment adjustment3 = new Adjustment(
                 r2.getSbn().getPackageName(), r2.getKey(), signals, "",
                 r2.getUser().getIdentifier());
         mBinderService.applyEnqueuedAdjustmentFromAssistant(null, adjustment3);
-        assertTrue(mService.checkLastClassificationChannelLog(true /*hasPosted*/,
-                false /*isAlerting*/, 1 /*TYPE_PROMOTION*/, 345));
+        assertTrue(mService.checkLastClassificationChannelLog(true /*=hasPosted*/,
+                false /*=isAlerting*/, Adjustment.TYPE_PROMOTION, 345,
+                NOTIFICATION_ADJUSTED.getId(),
+                instanceId2.getId() /*instance_id*/, r2.getUid()));
     }
 
     @Test
@@ -16703,6 +16712,10 @@
         when(mResources.getResourceName(eq(iconResId))).thenReturn(iconResName);
         when(mResources.getIdentifier(eq(iconResName), any(), any())).thenReturn(iconResId);
         when(mPackageManagerClient.getResourcesForApplication(eq(pkg))).thenReturn(mResources);
+
+        // Ensure that there is a zen configuration for the user running the test (won't be
+        // USER_SYSTEM if running on HSUM).
+        mService.mZenModeHelper.onUserSwitched(mUserId);
     }
 
     @Test
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 80e86a1..fbd53f7 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -3169,7 +3169,7 @@
 
         doThrow(new SecurityException("no access")).when(mUgmInternal)
                 .checkGrantUriPermission(eq(UID_N_MR1), any(), eq(sound),
-                    anyInt(), eq(Process.myUserHandle().getIdentifier()));
+                    anyInt(), eq(UserHandle.getUserId(UID_N_MR1)));
 
         final NotificationChannel channel = new NotificationChannel("id2", "name2",
                 NotificationManager.IMPORTANCE_DEFAULT);
@@ -3189,7 +3189,7 @@
 
         doThrow(new SecurityException("no access")).when(mUgmInternal)
                 .checkGrantUriPermission(eq(UID_N_MR1), any(), any(),
-                    anyInt(), eq(Process.myUserHandle().getIdentifier()));
+                    anyInt(), eq(UserHandle.getUserId(UID_N_MR1)));
 
         final NotificationChannel channel = new NotificationChannel("id2", "name2",
                 NotificationManager.IMPORTANCE_DEFAULT);
@@ -3208,7 +3208,7 @@
 
         doThrow(new SecurityException("no access")).when(mUgmInternal)
                 .checkGrantUriPermission(eq(UID_N_MR1), any(), any(),
-                    anyInt(), eq(Process.myUserHandle().getIdentifier()));
+                    anyInt(), eq(UserHandle.getUserId(UID_N_MR1)));
 
         final NotificationChannel channel = new NotificationChannel("id2", "name2",
                 NotificationManager.IMPORTANCE_DEFAULT);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/TestableNotificationManagerService.java b/services/tests/uiservicestests/src/com/android/server/notification/TestableNotificationManagerService.java
index ba91ca2..b434819 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/TestableNotificationManagerService.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/TestableNotificationManagerService.java
@@ -16,6 +16,8 @@
 
 package com.android.server.notification;
 
+import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
+
 import android.companion.ICompanionDeviceManager;
 import android.content.ComponentName;
 import android.content.Context;
@@ -25,6 +27,7 @@
 
 import com.android.internal.logging.InstanceIdSequence;
 import com.android.server.notification.ManagedServices.ManagedServiceInfo;
+import com.android.server.notification.NotificationRecordLogger.NotificationReportedEvent;
 
 import java.util.HashSet;
 import java.util.Set;
@@ -57,6 +60,9 @@
         public boolean isAlerting;
         public long classification;
         public long lifetime;
+        public long eventId;
+        public long instanceId;
+        public long uid;
     }
     public ClassificationChannelLog  lastClassificationChannelLog = null;
 
@@ -221,20 +227,34 @@
     }
 
     @Override
-    protected void logClassificationChannelAdjustmentReceived(boolean hasPosted, boolean isAlerting,
-                                                              int classification, int lifetimeMs) {
+    protected void logClassificationChannelAdjustmentReceived(NotificationRecord r,
+                                                              boolean hasPosted,
+                                                              int classification) {
+
+        boolean isAlerting = r.getChannel().getImportance() >= IMPORTANCE_DEFAULT;
+        int instanceId = r.getSbn().getInstanceId() == null
+                ? 0 : r.getSbn().getInstanceId().getId();
+        int lifetimeMs = r.getLifespanMs(System.currentTimeMillis());
+        int uid = r.getUid();
+
         lastClassificationChannelLog = new ClassificationChannelLog();
         lastClassificationChannelLog.hasPosted = hasPosted;
         lastClassificationChannelLog.isAlerting = isAlerting;
         lastClassificationChannelLog.classification = classification;
         lastClassificationChannelLog.lifetime = lifetimeMs;
+        lastClassificationChannelLog.eventId =
+                NotificationReportedEvent.NOTIFICATION_ADJUSTED.getId();
+        lastClassificationChannelLog.instanceId = instanceId;
+        lastClassificationChannelLog.uid = uid;
     }
 
     /**
      * Returns true if the last recorded classification channel log has all the values specified.
      */
     public boolean checkLastClassificationChannelLog(boolean hasPosted, boolean isAlerting,
-                                                     int classification, int lifetime) {
+                                                     int classification, int lifetime,
+                                                     int eventId, int instanceId,
+                                                     int uid) {
         if (lastClassificationChannelLog == null) {
             return false;
         }
@@ -242,6 +262,9 @@
         return hasPosted == lastClassificationChannelLog.hasPosted
                 && isAlerting == lastClassificationChannelLog.isAlerting
                 && classification == lastClassificationChannelLog.classification
-                && lifetime == lastClassificationChannelLog.lifetime;
+                && lifetime == lastClassificationChannelLog.lifetime
+                && eventId == lastClassificationChannelLog.eventId
+                && instanceId == lastClassificationChannelLog.instanceId
+                && uid == lastClassificationChannelLog.uid;
     }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenDeviceEffectsTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenDeviceEffectsTest.java
index 3ac7890..af911e8 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenDeviceEffectsTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenDeviceEffectsTest.java
@@ -49,18 +49,22 @@
 
     @Test
     public void builder() {
-        ZenDeviceEffects deviceEffects = new ZenDeviceEffects.Builder()
-                .setShouldDimWallpaper(true)
-                .setShouldDisableTapToWake(true).setShouldDisableTapToWake(false)
-                .setShouldDisableTiltToWake(true)
-                .setShouldMaximizeDoze(true)
-                .setShouldUseNightMode(false)
-                .setShouldSuppressAmbientDisplay(false).setShouldSuppressAmbientDisplay(true)
-                .addExtraEffect("WILL BE GONE")
-                .setExtraEffects(ImmutableSet.of("1", "2"))
-                .addExtraEffects(ImmutableSet.of("3", "4"))
-                .addExtraEffect("5")
-                .build();
+        ZenDeviceEffects deviceEffects =
+                new ZenDeviceEffects.Builder()
+                        .setShouldDimWallpaper(true)
+                        .setShouldDisableTapToWake(true)
+                        .setShouldDisableTapToWake(false)
+                        .setShouldDisableTiltToWake(true)
+                        .setShouldMaximizeDoze(true)
+                        .setShouldUseNightMode(false)
+                        .setShouldUseNightLight(true)
+                        .setShouldSuppressAmbientDisplay(false)
+                        .setShouldSuppressAmbientDisplay(true)
+                        .addExtraEffect("WILL BE GONE")
+                        .setExtraEffects(ImmutableSet.of("1", "2"))
+                        .addExtraEffects(ImmutableSet.of("3", "4"))
+                        .addExtraEffect("5")
+                        .build();
 
         assertThat(deviceEffects.shouldDimWallpaper()).isTrue();
         assertThat(deviceEffects.shouldDisableAutoBrightness()).isFalse();
@@ -68,6 +72,7 @@
         assertThat(deviceEffects.shouldDisableTiltToWake()).isTrue();
         assertThat(deviceEffects.shouldDisableTouch()).isFalse();
         assertThat(deviceEffects.shouldDisplayGrayscale()).isFalse();
+        assertThat(deviceEffects.shouldUseNightLight()).isTrue();
         assertThat(deviceEffects.shouldMaximizeDoze()).isTrue();
         assertThat(deviceEffects.shouldMinimizeRadioUsage()).isFalse();
         assertThat(deviceEffects.shouldUseNightMode()).isFalse();
@@ -85,15 +90,18 @@
                 .addExtraEffect("1")
                 .build();
 
-        ZenDeviceEffects modified = new ZenDeviceEffects.Builder(original)
-                .setShouldDisplayGrayscale(true)
-                .setShouldUseNightMode(false)
-                .addExtraEffect("2")
-                .build();
+        ZenDeviceEffects modified =
+                new ZenDeviceEffects.Builder(original)
+                        .setShouldDisplayGrayscale(true)
+                        .setShouldUseNightMode(false)
+                        .setShouldUseNightLight(true)
+                        .addExtraEffect("2")
+                        .build();
 
         assertThat(modified.shouldDimWallpaper()).isTrue(); // from original
         assertThat(modified.shouldDisableTiltToWake()).isTrue(); // from original
         assertThat(modified.shouldDisplayGrayscale()).isTrue(); // updated
+        assertThat(modified.shouldUseNightLight()).isTrue(); // updated
         assertThat(modified.shouldUseNightMode()).isFalse(); // updated
         assertThat(modified.shouldSuppressAmbientDisplay()).isTrue(); // from original
         assertThat(modified.getExtraEffects()).containsExactly("1", "2"); // updated
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 3236f95..b42a6a5 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
@@ -18,7 +18,6 @@
 
 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;
@@ -26,7 +25,6 @@
 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;
@@ -56,9 +54,7 @@
 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;
 
@@ -71,7 +67,6 @@
 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;
@@ -100,6 +95,9 @@
 import org.mockito.MockitoAnnotations;
 import org.xmlpull.v1.XmlPullParserException;
 
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
+import platform.test.runner.parameterized.Parameters;
+
 import java.io.BufferedInputStream;
 import java.io.BufferedOutputStream;
 import java.io.ByteArrayInputStream;
@@ -108,9 +106,6 @@
 import java.time.Instant;
 import java.util.List;
 
-import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
-import platform.test.runner.parameterized.Parameters;
-
 @SmallTest
 @RunWith(ParameterizedAndroidJunit4.class)
 public class ZenModeConfigTest extends UiServiceTestCase {
@@ -731,19 +726,21 @@
         rule.setConditionOverride(OVERRIDE_DEACTIVATE);
         rule.pkg = OWNER.getPackageName();
         rule.zenPolicy = POLICY;
-        rule.zenDeviceEffects = new ZenDeviceEffects.Builder()
-                .setShouldDisplayGrayscale(false)
-                .setShouldSuppressAmbientDisplay(true)
-                .setShouldDimWallpaper(false)
-                .setShouldUseNightMode(true)
-                .setShouldDisableAutoBrightness(false)
-                .setShouldDisableTapToWake(true)
-                .setShouldDisableTiltToWake(false)
-                .setShouldDisableTouch(true)
-                .setShouldMinimizeRadioUsage(false)
-                .setShouldMaximizeDoze(true)
-                .setExtraEffects(ImmutableSet.of("one", "two"))
-                .build();
+        rule.zenDeviceEffects =
+                new ZenDeviceEffects.Builder()
+                        .setShouldDisplayGrayscale(false)
+                        .setShouldSuppressAmbientDisplay(true)
+                        .setShouldDimWallpaper(false)
+                        .setShouldUseNightMode(true)
+                        .setShouldDisableAutoBrightness(false)
+                        .setShouldDisableTapToWake(true)
+                        .setShouldDisableTiltToWake(false)
+                        .setShouldDisableTouch(true)
+                        .setShouldMinimizeRadioUsage(false)
+                        .setShouldMaximizeDoze(true)
+                        .setShouldUseNightLight(true)
+                        .setExtraEffects(ImmutableSet.of("one", "two"))
+                        .build();
         rule.creationTime = CREATION_TIME;
 
         rule.allowManualInvocation = ALLOW_MANUAL;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java
index c6cc941..b138c72 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java
@@ -54,6 +54,9 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
+import platform.test.runner.parameterized.Parameters;
+
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Field;
 import java.lang.reflect.Method;
@@ -67,10 +70,6 @@
 import java.util.Optional;
 import java.util.Set;
 
-import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
-import platform.test.runner.parameterized.Parameters;
-
-
 @SmallTest
 @RunWith(ParameterizedAndroidJunit4.class)
 @TestableLooper.RunWithLooper
@@ -232,6 +231,7 @@
                 + "mDisableTouch:true->false, "
                 + "mMinimizeRadioUsage:true->false, "
                 + "mMaximizeDoze:true->false, "
+                + "mNightLight:true->false, "
                 + "mExtraEffects:[effect1]->[effect2]}, "
                 + "triggerDescription:string1->string2, "
                 + "type:2->1, "
@@ -358,18 +358,21 @@
         generateFieldDiffs(effects1, effects2, fieldsForDiff, expectedFrom, expectedTo);
 
         d = new ZenModeDiff.DeviceEffectsDiff(effects1, effects2);
-        assertThat(d.toString()).isEqualTo("ZenDeviceEffectsDiff{"
-                + "mGrayscale:true->false, "
-                + "mSuppressAmbientDisplay:true->false, "
-                + "mDimWallpaper:true->false, "
-                + "mNightMode:true->false, "
-                + "mDisableAutoBrightness:true->false, "
-                + "mDisableTapToWake:true->false, "
-                + "mDisableTiltToWake:true->false, "
-                + "mDisableTouch:true->false, "
-                + "mMinimizeRadioUsage:true->false, "
-                + "mMaximizeDoze:true->false, "
-                + "mExtraEffects:[effect1]->[effect2]}");
+        assertThat(d.toString())
+                .isEqualTo(
+                        "ZenDeviceEffectsDiff{"
+                                + "mGrayscale:true->false, "
+                                + "mSuppressAmbientDisplay:true->false, "
+                                + "mDimWallpaper:true->false, "
+                                + "mNightMode:true->false, "
+                                + "mDisableAutoBrightness:true->false, "
+                                + "mDisableTapToWake:true->false, "
+                                + "mDisableTiltToWake:true->false, "
+                                + "mDisableTouch:true->false, "
+                                + "mMinimizeRadioUsage:true->false, "
+                                + "mMaximizeDoze:true->false, "
+                                + "mNightLight:true->false, "
+                                + "mExtraEffects:[effect1]->[effect2]}");
     }
 
 
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 09da015..1884bbd 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -202,6 +202,9 @@
 import org.mockito.MockitoAnnotations;
 import org.xmlpull.v1.XmlPullParserException;
 
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
+import platform.test.runner.parameterized.Parameters;
+
 import java.io.BufferedInputStream;
 import java.io.BufferedOutputStream;
 import java.io.ByteArrayInputStream;
@@ -221,9 +224,6 @@
 import java.util.concurrent.TimeUnit;
 import java.util.stream.Collectors;
 
-import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
-import platform.test.runner.parameterized.Parameters;
-
 @SmallTest
 @SuppressLint("GuardedBy") // It's ok for this test to access guarded methods from the service.
 @RunWith(ParameterizedAndroidJunit4.class)
@@ -2759,18 +2759,20 @@
     @Test
     @EnableFlags(FLAG_MODES_API)
     public void addAutomaticZenRule_fromApp_ignoresHiddenEffects() {
-        ZenDeviceEffects zde = new ZenDeviceEffects.Builder()
-                .setShouldDisplayGrayscale(true)
-                .setShouldSuppressAmbientDisplay(true)
-                .setShouldDimWallpaper(true)
-                .setShouldUseNightMode(true)
-                .setShouldDisableAutoBrightness(true)
-                .setShouldDisableTapToWake(true)
-                .setShouldDisableTiltToWake(true)
-                .setShouldDisableTouch(true)
-                .setShouldMinimizeRadioUsage(true)
-                .setShouldMaximizeDoze(true)
-                .build();
+        ZenDeviceEffects zde =
+                new ZenDeviceEffects.Builder()
+                        .setShouldDisplayGrayscale(true)
+                        .setShouldSuppressAmbientDisplay(true)
+                        .setShouldDimWallpaper(true)
+                        .setShouldUseNightMode(true)
+                        .setShouldDisableAutoBrightness(true)
+                        .setShouldDisableTapToWake(true)
+                        .setShouldDisableTiltToWake(true)
+                        .setShouldDisableTouch(true)
+                        .setShouldMinimizeRadioUsage(true)
+                        .setShouldMaximizeDoze(true)
+                        .setShouldUseNightLight(true)
+                        .build();
 
         String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
                 mContext.getPackageName(),
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 41865b20..9d4d94b 100644
--- a/services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java
@@ -26,12 +26,12 @@
 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;
 import android.platform.test.annotations.EnableFlags;
 import android.platform.test.annotations.Presubmit;
+import android.provider.Settings;
 import android.view.KeyEvent;
 
 import androidx.test.filters.MediumTest;
@@ -758,54 +758,57 @@
     }
 
     @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() {
+    public void testKeyGestureToggleDoNotDisturb() {
+        mPhoneWindowManager.overrideZenMode(Settings.Global.ZEN_MODE_OFF);
         Assert.assertTrue(
-                sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_STICKY_KEYS));
-        Assert.assertTrue(InputSettings.isAccessibilityStickyKeysEnabled(mContext));
+                sendKeyGestureEventComplete(
+                        KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_DO_NOT_DISTURB));
+        mPhoneWindowManager.assertZenMode(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS);
 
+        mPhoneWindowManager.overrideZenMode(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS);
         Assert.assertTrue(
-                sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_STICKY_KEYS));
-        Assert.assertFalse(InputSettings.isAccessibilityStickyKeysEnabled(mContext));
+                sendKeyGestureEventComplete(
+                        KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_DO_NOT_DISTURB));
+        mPhoneWindowManager.assertZenMode(Settings.Global.ZEN_MODE_OFF);
     }
 
     @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));
+    public void testLaunchSettingsAndSearchDoesntOpenAnything_withKeyguardOn() {
+        mPhoneWindowManager.overrideKeyguardOn(true);
 
-        Assert.assertTrue(
-                sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_SLOW_KEYS));
-        Assert.assertFalse(InputSettings.isAccessibilitySlowKeysEnabled(mContext));
+        sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS);
+        sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SEARCH);
+
+        mPhoneWindowManager.assertNoActivityLaunched();
     }
 
     @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));
+    public void testLaunchSettingsAndSearchDoesntOpenAnything_withUserSetupIncomplete() {
+        mPhoneWindowManager.overrideIsUserSetupComplete(false);
 
-        Assert.assertTrue(
-                sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MOUSE_KEYS));
-        Assert.assertFalse(InputSettings.isAccessibilityMouseKeysEnabled(mContext));
+        sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS);
+        sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SEARCH);
+
+        mPhoneWindowManager.assertNoActivityLaunched();
     }
 
     @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));
+    public void testLaunchAssistantDoesntWork_withKeyguardOn() {
+        mPhoneWindowManager.overrideKeyguardOn(true);
 
-        Assert.assertTrue(
-                sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_BOUNCE_KEYS));
-        Assert.assertFalse(InputSettings.isAccessibilityBounceKeysEnabled(mContext));
+        sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_ASSISTANT);
+        sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_VOICE_ASSISTANT);
+
+        mPhoneWindowManager.assertSearchManagerDoesntLaunchAssist();
+    }
+
+    @Test
+    public void testLaunchAssistantDoesntWork_withUserSetupIncomplete() {
+        mPhoneWindowManager.overrideIsUserSetupComplete(false);
+
+        sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_ASSISTANT);
+        sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_VOICE_ASSISTANT);
+
+        mPhoneWindowManager.assertSearchManagerDoesntLaunchAssist();
     }
 }
diff --git a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
index 9db76d4..285d94d 100644
--- a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
+++ b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
@@ -447,6 +447,14 @@
         mTestLooper.dispatchAll();
     }
 
+    void overrideZenMode(int mode) {
+        doReturn(mode).when(mNotificationManager).getZenMode();
+    }
+
+    void assertZenMode(int mode) {
+        verify(mNotificationManager).setZenMode(eq(mode), any(), anyString(), eq(true));
+    }
+
     /**
      * Below functions will override the setting or the policy behavior.
      */
@@ -563,6 +571,10 @@
         doNothing().when(mPhoneWindowManager).launchHomeFromHotKey(anyInt());
     }
 
+    void overrideKeyguardOn(boolean isKeyguardOn) {
+        doReturn(isKeyguardOn).when(mPhoneWindowManager).keyguardOn();
+    }
+
     void overrideIsUserSetupComplete(boolean isCompleted) {
         doReturn(isCompleted).when(mPhoneWindowManager).isUserSetupComplete();
     }
@@ -725,6 +737,11 @@
         verify(mSearchManager).launchAssist(any());
     }
 
+    void assertSearchManagerDoesntLaunchAssist() {
+        mTestLooper.dispatchAll();
+        verify(mSearchManager, never()).launchAssist(any());
+    }
+
     void assertLaunchSystemSettings() {
         mTestLooper.dispatchAll();
         ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
@@ -929,4 +946,10 @@
         verify(mInputManagerInternal)
                 .handleKeyGestureInKeyGestureController(anyInt(), any(), anyInt(), eq(gestureType));
     }
+
+    void assertNoActivityLaunched() {
+        mTestLooper.dispatchAll();
+        verify(mContext, never()).startActivityAsUser(any(), any(), any());
+        verify(mContext, never()).startActivityAsUser(any(), any());
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 1b0d9dc..c9cbe0f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -32,6 +32,8 @@
 import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_DEFAULT;
 import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_IF_ALLOWLISTED;
 import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_NEVER;
+import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
@@ -124,6 +126,7 @@
 import android.app.servertransaction.DestroyActivityItem;
 import android.app.servertransaction.PauseActivityItem;
 import android.app.servertransaction.WindowStateResizeItem;
+import android.compat.testing.PlatformCompatChangeRule;
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
@@ -138,6 +141,7 @@
 import android.os.PersistableBundle;
 import android.os.Process;
 import android.os.RemoteException;
+import android.platform.test.annotations.EnableFlags;
 import android.platform.test.annotations.Presubmit;
 import android.provider.DeviceConfig;
 import android.util.MutableBoolean;
@@ -159,9 +163,13 @@
 import com.android.server.wm.ActivityRecord.State;
 import com.android.window.flags.Flags;
 
+import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
+
 import org.junit.Assert;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.TestRule;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
 import org.mockito.invocation.InvocationOnMock;
@@ -183,6 +191,9 @@
 @RunWith(WindowTestRunner.class)
 public class ActivityRecordTests extends WindowTestsBase {
 
+    @Rule
+    public TestRule compatChangeRule = new PlatformCompatChangeRule();
+
     private final String mPackageName = getInstrumentation().getTargetContext().getPackageName();
 
     private static final int ORIENTATION_CONFIG_CHANGES =
@@ -721,6 +732,64 @@
     }
 
     @Test
+    @EnableFlags(Flags.FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
+    @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT})
+    public void testOrientation_allowFixedOrientationForCameraCompatInFreeformWindowing() {
+        final ActivityRecord activity = setupDisplayAndActivityForCameraCompat(
+                /* isCameraRunning= */ true, WINDOWING_MODE_FREEFORM);
+
+        // Task in landscape.
+        assertEquals(ORIENTATION_LANDSCAPE, activity.getTask().getConfiguration().orientation);
+        // The app should be letterboxed.
+        assertEquals(ORIENTATION_PORTRAIT, activity.getConfiguration().orientation);
+        assertTrue(activity.mAppCompatController.getAppCompatAspectRatioPolicy()
+                .isLetterboxedForFixedOrientationAndAspectRatio());
+    }
+
+    @Test
+    public void testOrientation_dontAllowFixedOrientationForCameraCompatFreeformIfNotEnabled() {
+        final ActivityRecord activity = setupDisplayAndActivityForCameraCompat(
+                /* isCameraRunning= */ true, WINDOWING_MODE_FREEFORM);
+
+        // Task in landscape.
+        assertEquals(ORIENTATION_LANDSCAPE, activity.getTask().getConfiguration().orientation);
+        // Activity is not letterboxed.
+        assertEquals(ORIENTATION_LANDSCAPE, activity.getConfiguration().orientation);
+        assertFalse(activity.mAppCompatController.getAppCompatAspectRatioPolicy()
+                .isLetterboxedForFixedOrientationAndAspectRatio());
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
+    @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT})
+    public void testOrientation_noFixedOrientationForCameraCompatFreeformIfCameraNotRunning() {
+        final ActivityRecord activity = setupDisplayAndActivityForCameraCompat(
+                /* isCameraRunning= */ false, WINDOWING_MODE_FREEFORM);
+
+        // Task in landscape.
+        assertEquals(ORIENTATION_LANDSCAPE, activity.getTask().getConfiguration().orientation);
+        // Activity is not letterboxed.
+        assertEquals(ORIENTATION_LANDSCAPE, activity.getConfiguration().orientation);
+        assertFalse(activity.mAppCompatController.getAppCompatAspectRatioPolicy()
+                .isLetterboxedForFixedOrientationAndAspectRatio());
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
+    @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT})
+    public void testOrientation_dontAllowFixedOrientationForCameraCompatFreeformIfInPip() {
+        final ActivityRecord activity = setupDisplayAndActivityForCameraCompat(
+                /* isCameraRunning= */ true, WINDOWING_MODE_PINNED);
+
+        // Task in landscape.
+        assertEquals(ORIENTATION_LANDSCAPE, activity.getTask().getConfiguration().orientation);
+        // Activity is not letterboxed.
+        assertEquals(ORIENTATION_LANDSCAPE, activity.getConfiguration().orientation);
+        assertFalse(activity.mAppCompatController.getAppCompatAspectRatioPolicy()
+                .isLetterboxedForFixedOrientationAndAspectRatio());
+    }
+
+    @Test
     public void testShouldMakeActive_deferredResume() {
         final ActivityRecord activity = createActivityWithTask();
         activity.setState(STOPPED, "Testing");
@@ -2472,11 +2541,14 @@
         final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build();
         assertEquals(0, activity.getChildCount());
 
-        final WindowState win1 = createWindow(null, TYPE_APPLICATION, activity, "win1");
-        final WindowState startingWin = createWindow(null, TYPE_APPLICATION_STARTING, activity,
-                "startingWin");
-        final WindowState baseWin = createWindow(null, TYPE_BASE_APPLICATION, activity, "baseWin");
-        final WindowState win4 = createWindow(null, TYPE_APPLICATION, activity, "win4");
+        final WindowState win1 = newWindowBuilder("app1", TYPE_APPLICATION).setWindowToken(
+                activity).build();
+        final WindowState startingWin = newWindowBuilder("startingWin",
+                TYPE_APPLICATION_STARTING).setWindowToken(activity).build();
+        final WindowState baseWin = newWindowBuilder("baseWin",
+                TYPE_BASE_APPLICATION).setWindowToken(activity).build();
+        final WindowState win4 = newWindowBuilder("win4", TYPE_APPLICATION).setWindowToken(
+                activity).build();
 
         // Should not contain the windows that were added above.
         assertEquals(4, activity.getChildCount());
@@ -2499,14 +2571,17 @@
         final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build();
         assertNull(activity.findMainWindow());
 
-        final WindowState window1 = createWindow(null, TYPE_BASE_APPLICATION, activity, "window1");
-        final WindowState window11 = createWindow(window1, FIRST_SUB_WINDOW, activity, "window11");
-        final WindowState window12 = createWindow(window1, FIRST_SUB_WINDOW, activity, "window12");
+        final WindowState window1 = newWindowBuilder("window1",
+                TYPE_BASE_APPLICATION).setWindowToken(activity).build();
+        final WindowState window11 = newWindowBuilder("window11", FIRST_SUB_WINDOW).setParent(
+                window1).setWindowToken(activity).build();
+        final WindowState window12 = newWindowBuilder("window12", FIRST_SUB_WINDOW).setParent(
+                window1).setWindowToken(activity).build();
         assertEquals(window1, activity.findMainWindow());
         window1.mAnimatingExit = true;
         assertEquals(window1, activity.findMainWindow());
-        final WindowState window2 = createWindow(null, TYPE_APPLICATION_STARTING, activity,
-                "window2");
+        final WindowState window2 = newWindowBuilder("window2",
+                TYPE_APPLICATION_STARTING).setWindowToken(activity).build();
         assertEquals(window2, activity.findMainWindow());
         activity.removeImmediately();
     }
@@ -2651,8 +2726,8 @@
 
     @Test
     public void testStuckExitingWindow() {
-        final WindowState closingWindow = createWindow(null, FIRST_APPLICATION_WINDOW,
-                "closingWindow");
+        final WindowState closingWindow = newWindowBuilder("closingWindow",
+                FIRST_APPLICATION_WINDOW).build();
         closingWindow.mAnimatingExit = true;
         closingWindow.mRemoveOnExit = true;
         closingWindow.mActivityRecord.commitVisibility(
@@ -3253,7 +3328,6 @@
         assertFalse(activity.isVisibleRequested());
 
         player.start();
-        mSetFlagsRule.enableFlags(Flags.FLAG_RESET_DRAW_STATE_ON_CLIENT_INVISIBLE);
         // ActivityRecord#commitVisibility(false) -> WindowState#sendAppVisibilityToClients().
         player.finish();
         assertFalse(activity.isVisible());
@@ -3314,7 +3388,7 @@
     @SetupWindows(addWindows = W_INPUT_METHOD)
     @Test
     public void testImeInsetsFrozenFlag_resetWhenNoImeFocusableInActivity() {
-        final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+        final WindowState app = newWindowBuilder("app", TYPE_APPLICATION).build();
         makeWindowVisibleAndDrawn(app, mImeWindow);
         mDisplayContent.setImeLayeringTarget(app);
         mDisplayContent.setImeInputTarget(app);
@@ -3342,7 +3416,7 @@
     @SetupWindows(addWindows = W_INPUT_METHOD)
     @Test
     public void testImeInsetsFrozenFlag_resetWhenReportedToBeImeInputTarget() {
-        final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+        final WindowState app = newWindowBuilder("app", TYPE_APPLICATION).build();
 
         mDisplayContent.getInsetsStateController().getImeSourceProvider().setWindowContainer(
                 mImeWindow, null, null);
@@ -3386,8 +3460,8 @@
     @Test
     public void testImeInsetsFrozenFlag_noDispatchVisibleInsetsWhenAppNotRequest()
             throws RemoteException {
-        final WindowState app1 = createWindow(null, TYPE_APPLICATION, "app1");
-        final WindowState app2 = createWindow(null, TYPE_APPLICATION, "app2");
+        final WindowState app1 = newWindowBuilder("app1", TYPE_APPLICATION).build();
+        final WindowState app2 = newWindowBuilder("app2", TYPE_APPLICATION).build();
 
         mDisplayContent.getInsetsStateController().getImeSourceProvider().setWindowContainer(
                 mImeWindow, null, null);
@@ -3431,7 +3505,8 @@
     @Test
     public void testImeInsetsFrozenFlag_multiWindowActivities() {
         final WindowToken imeToken = createTestWindowToken(TYPE_INPUT_METHOD, mDisplayContent);
-        final WindowState ime = createWindow(null, TYPE_INPUT_METHOD, imeToken, "ime");
+        final WindowState ime = newWindowBuilder("ime", TYPE_INPUT_METHOD).setWindowToken(
+                imeToken).build();
         makeWindowVisibleAndDrawn(ime);
 
         // Create a split-screen root task with activity1 and activity 2.
@@ -3452,8 +3527,10 @@
         activity1.mImeInsetsFrozenUntilStartInput = true;
         activity2.mImeInsetsFrozenUntilStartInput = true;
 
-        final WindowState app1 = createWindow(null, TYPE_APPLICATION, activity1, "app1");
-        final WindowState app2 = createWindow(null, TYPE_APPLICATION, activity2, "app2");
+        final WindowState app1 = newWindowBuilder("app1", TYPE_APPLICATION).setWindowToken(
+                activity1).build();
+        final WindowState app2 = newWindowBuilder("app2", TYPE_APPLICATION).setWindowToken(
+                activity2).build();
         makeWindowVisibleAndDrawn(app1, app2);
 
         final InsetsStateController controller = mDisplayContent.getInsetsStateController();
@@ -3482,7 +3559,7 @@
 
     @Test
     public void testInClosingAnimation_visibilityNotCommitted_doNotHideSurface() {
-        final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+        final WindowState app = newWindowBuilder("app", TYPE_APPLICATION).build();
         makeWindowVisibleAndDrawn(app);
 
         // Put the activity in close transition.
@@ -3509,7 +3586,7 @@
 
     @Test
     public void testInClosingAnimation_visibilityCommitted_hideSurface() {
-        final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+        final WindowState app = newWindowBuilder("app", TYPE_APPLICATION).build();
         makeWindowVisibleAndDrawn(app);
         app.mActivityRecord.prepareSurfaces();
 
@@ -3696,6 +3773,37 @@
         assertTrue(appWindow.mResizeReported);
     }
 
+    private ActivityRecord setupDisplayAndActivityForCameraCompat(boolean isCameraRunning,
+            int windowingMode) {
+        doReturn(true).when(() -> DesktopModeHelper.canEnterDesktopMode(any()));
+        // Create a new DisplayContent so that the flag values create the camera freeform policy.
+        mDisplayContent = new TestDisplayContent.Builder(mAtm, mDisplayContent.getSurfaceWidth(),
+                mDisplayContent.getSurfaceHeight()).build();
+        final CameraStateMonitor cameraStateMonitor = mDisplayContent.mAppCompatCameraPolicy
+                .mCameraStateMonitor;
+        spyOn(cameraStateMonitor);
+        doReturn(isCameraRunning).when(cameraStateMonitor).isCameraRunningForActivity(any());
+        final TaskDisplayArea tda = mDisplayContent.getDefaultTaskDisplayArea();
+        spyOn(tda);
+        doReturn(true).when(tda).supportsNonResizableMultiWindow();
+        final Task rootTask = new TaskBuilder(mSupervisor).setDisplay(mDisplayContent)
+                .setWindowingMode(windowingMode).build();
+        doReturn(mDisplayContent.getDisplayInfo())
+                .when(mDisplayContent.mWmService.mDisplayManagerInternal).getDisplayInfo(anyInt());
+        rootTask.setBounds(0, 0, 1000, 500);
+        final ActivityRecord activity = new ActivityBuilder(mAtm)
+                .setComponent(ComponentName.createRelative(mContext,
+                        com.android.server.wm.ActivityRecordTests.class.getName()))
+                .setParentTask(rootTask)
+                .setCreateTask(true)
+                .setOnTop(true)
+                .setResizeMode(RESIZE_MODE_RESIZEABLE)
+                .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)
+                .build();
+        activity.mAppCompatController.getAppCompatSizeCompatModePolicy().clearSizeCompatMode();
+        return activity;
+    }
+
     private void assertHasStartingWindow(ActivityRecord atoken) {
         assertNotNull(atoken.mStartingSurface);
         assertNotNull(atoken.mStartingData);
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatOrientationPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatOrientationPolicyTest.java
index 09ed9ba..90bf5f0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatOrientationPolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatOrientationPolicyTest.java
@@ -302,15 +302,15 @@
     }
 
     @Test
-    public void testOverrideOrientationIfNeeded_userFullscreenOverride_returnsUser() {
+    public void testOverrideOrientationIfNeeded_userFullscreenOverride_notLetterboxed_unchanged() {
         runTestScenarioWithActivity((robot) -> {
             robot.applyOnActivity((a) -> {
                 a.setShouldApplyUserFullscreenOverride(true);
                 a.setIgnoreOrientationRequest(true);
             });
 
-            robot.checkOverrideOrientation(/* candidate */ SCREEN_ORIENTATION_UNSPECIFIED,
-                    /* expected */ SCREEN_ORIENTATION_USER);
+            robot.checkOverrideOrientation(/* candidate */ SCREEN_ORIENTATION_LOCKED,
+                    /* expected */ SCREEN_ORIENTATION_LOCKED);
         });
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
index 579ed66..e0e8aa4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
@@ -447,8 +447,8 @@
 
     @Test
     public void backInfoWindowWithNoActivity() {
-        WindowState window = createWindow(null, WindowManager.LayoutParams.TYPE_WALLPAPER,
-                "Wallpaper");
+        WindowState window = newWindowBuilder("Wallpaper",
+                WindowManager.LayoutParams.TYPE_WALLPAPER).build();
         addToWindowMap(window, true);
         makeWindowVisibleAndDrawn(window);
 
@@ -468,8 +468,8 @@
 
     @Test
     public void backInfoWithAnimationCallback() {
-        WindowState window = createWindow(null, WindowManager.LayoutParams.TYPE_WALLPAPER,
-                "Wallpaper");
+        WindowState window = newWindowBuilder("Wallpaper",
+                WindowManager.LayoutParams.TYPE_WALLPAPER).build();
         addToWindowMap(window, true);
         makeWindowVisibleAndDrawn(window);
 
@@ -535,8 +535,8 @@
                 .build();
         testActivity.info.applicationInfo.privateFlagsExt |=
                 PRIVATE_FLAG_EXT_ENABLE_ON_BACK_INVOKED_CALLBACK;
-        final WindowState window = createWindow(null, TYPE_BASE_APPLICATION, testActivity,
-                "window");
+        final WindowState window = newWindowBuilder("window", TYPE_BASE_APPLICATION).setWindowToken(
+                testActivity).build();
         addToWindowMap(window, true);
         makeWindowVisibleAndDrawn(window);
         IOnBackInvokedCallback callback = withSystemCallback(testActivity.getTask());
@@ -610,8 +610,7 @@
 
     @Test
     public void backInfoWindowWithoutDrawn() {
-        WindowState window = createWindow(null, WindowManager.LayoutParams.TYPE_APPLICATION,
-                "TestWindow");
+        WindowState window = newWindowBuilder("TestWindow", TYPE_APPLICATION).build();
         addToWindowMap(window, true);
 
         IOnBackInvokedCallback callback = createOnBackInvokedCallback();
@@ -677,7 +676,7 @@
         assertEquals("change focus back, callback should not have been called",
                 1, navigationObserver.getCount());
 
-        WindowState newWindow = createWindow(null, TYPE_APPLICATION_OVERLAY, "overlayWindow");
+        WindowState newWindow = newWindowBuilder("overlayWindow", TYPE_APPLICATION_OVERLAY).build();
         addToWindowMap(newWindow, true);
         mBackNavigationController.onFocusChanged(newWindow);
         assertEquals("Focus change, callback should have been called",
@@ -902,7 +901,8 @@
         // enable OnBackInvokedCallbacks
         record.info.applicationInfo.privateFlagsExt |=
                 PRIVATE_FLAG_EXT_ENABLE_ON_BACK_INVOKED_CALLBACK;
-        WindowState window = createWindow(null, FIRST_APPLICATION_WINDOW, record, "window");
+        WindowState window = newWindowBuilder("window", FIRST_APPLICATION_WINDOW).setWindowToken(
+                record).build();
         when(record.mSurfaceControl.isValid()).thenReturn(true);
         Mockito.doNothing().when(task).reparentSurfaceControl(any(), any());
         mAtm.setFocusedTask(task.mTaskId, record);
@@ -918,8 +918,10 @@
         // enable OnBackInvokedCallbacks
         record.info.applicationInfo.privateFlagsExt |=
                 PRIVATE_FLAG_EXT_ENABLE_ON_BACK_INVOKED_CALLBACK;
-        WindowState window = createWindow(null, FIRST_APPLICATION_WINDOW, record, "window");
-        WindowState dialog = createWindow(null, TYPE_APPLICATION, record, "dialog");
+        WindowState window = newWindowBuilder("window", FIRST_APPLICATION_WINDOW).setWindowToken(
+                record).build();
+        WindowState dialog = newWindowBuilder("dialog", TYPE_APPLICATION).setWindowToken(
+                record).build();
         when(record.mSurfaceControl.isValid()).thenReturn(true);
         Mockito.doNothing().when(task).reparentSurfaceControl(any(), any());
         mAtm.setFocusedTask(task.mTaskId, record);
@@ -944,8 +946,10 @@
         // enable OnBackInvokedCallbacks
         record2.info.applicationInfo.privateFlagsExt |=
                 PRIVATE_FLAG_EXT_ENABLE_ON_BACK_INVOKED_CALLBACK;
-        WindowState window1 = createWindow(null, FIRST_APPLICATION_WINDOW, record1, "window1");
-        WindowState window2 = createWindow(null, FIRST_APPLICATION_WINDOW, record2, "window2");
+        WindowState window1 = newWindowBuilder("window1", FIRST_APPLICATION_WINDOW).setWindowToken(
+                record1).build();
+        WindowState window2 = newWindowBuilder("window2", FIRST_APPLICATION_WINDOW).setWindowToken(
+                record2).build();
         when(task.mSurfaceControl.isValid()).thenReturn(true);
         when(record1.mSurfaceControl.isValid()).thenReturn(true);
         when(record2.mSurfaceControl.isValid()).thenReturn(true);
diff --git a/services/tests/wmtests/src/com/android/server/wm/CameraCompatFreeformPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/CameraCompatFreeformPolicyTests.java
index 3750dd3..748a47a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/CameraCompatFreeformPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/CameraCompatFreeformPolicyTests.java
@@ -203,6 +203,58 @@
     }
 
     @Test
+    @DisableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
+    @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT})
+    public void testIsFreeformLetterboxingForCameraAllowed_featureDisabled_returnsFalse() {
+        configureActivity(SCREEN_ORIENTATION_PORTRAIT);
+
+        mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
+
+        assertFalse(mCameraCompatFreeformPolicy.isFreeformLetterboxingForCameraAllowed(mActivity));
+    }
+
+    @Test
+    @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
+    public void testIsFreeformLetterboxingForCameraAllowed_overrideDisabled_returnsFalse() {
+        configureActivity(SCREEN_ORIENTATION_PORTRAIT);
+
+        mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
+
+        assertFalse(mCameraCompatFreeformPolicy.isFreeformLetterboxingForCameraAllowed(mActivity));
+    }
+
+    @Test
+    @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
+    @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT})
+    public void testIsFreeformLetterboxingForCameraAllowed_cameraNotRunning_returnsFalse() {
+        configureActivity(SCREEN_ORIENTATION_PORTRAIT);
+
+        assertFalse(mCameraCompatFreeformPolicy.isFreeformLetterboxingForCameraAllowed(mActivity));
+    }
+
+    @Test
+    @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
+    @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT})
+    public void testIsFreeformLetterboxingForCameraAllowed_notFreeformWindowing_returnsFalse() {
+        configureActivity(SCREEN_ORIENTATION_PORTRAIT, WINDOWING_MODE_FULLSCREEN);
+
+        mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
+
+        assertFalse(mCameraCompatFreeformPolicy.isFreeformLetterboxingForCameraAllowed(mActivity));
+    }
+
+    @Test
+    @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
+    @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT})
+    public void testIsFreeformLetterboxingForCameraAllowed_optInFreeformCameraRunning_true() {
+        configureActivity(SCREEN_ORIENTATION_PORTRAIT);
+
+        mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
+
+        assertTrue(mCameraCompatFreeformPolicy.isFreeformLetterboxingForCameraAllowed(mActivity));
+    }
+
+    @Test
     @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
     @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT})
     public void testFullscreen_doesNotActivateCameraCompatMode() {
diff --git a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
index f339d29..429a396a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
@@ -92,7 +92,7 @@
  * Tests for the {@link DragDropController} class.
  *
  * Build/Install/Run:
- *  atest WmTests:DragDropControllerTests
+ * atest WmTests:DragDropControllerTests
  */
 @SmallTest
 @Presubmit
@@ -146,12 +146,12 @@
      */
     private WindowState createDropTargetWindow(String name, int ownerId) {
         final Task task = new TaskBuilder(mSupervisor).setUserId(ownerId).build();
-        final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task)
-                .setUseProcess(mProcess).build();
+        final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task).setUseProcess(
+                mProcess).build();
 
         // Use a new TestIWindow so we don't collect events for other windows
-        final WindowState window = createWindow(
-                null, TYPE_BASE_APPLICATION, activity, name, ownerId, false, new TestIWindow());
+        final WindowState window = createWindow(null, TYPE_BASE_APPLICATION, activity, name,
+                ownerId, false, new TestIWindow());
         InputChannel channel = new InputChannel();
         window.openInputChannel(channel);
         window.mHasSurface = true;
@@ -173,12 +173,11 @@
     @Before
     public void setUp() throws Exception {
         mTarget = new TestDragDropController(mWm, mWm.mH.getLooper());
-        mProcess = mSystemServicesTestRule.addProcess(TEST_PACKAGE, "testProc",
-                TEST_PID, TEST_UID);
+        mProcess = mSystemServicesTestRule.addProcess(TEST_PACKAGE, "testProc", TEST_PID, TEST_UID);
         mWindow = createDropTargetWindow("Drag test window", 0);
         doReturn(mWindow).when(mDisplayContent).getTouchableWinAtPointLocked(0, 0);
-        when(mWm.mInputManager.startDragAndDrop(any(IBinder.class),
-                any(IBinder.class))).thenReturn(true);
+        when(mWm.mInputManager.startDragAndDrop(any(IBinder.class), any(IBinder.class))).thenReturn(
+                true);
 
         mWm.mWindowMap.put(mWindow.mClient.asBinder(), mWindow);
     }
@@ -286,16 +285,15 @@
                     // Verify the start-drag event is sent for the local and global intercept window
                     // but not the other window
                     assertTrue(nonLocalWindowDragEvents.isEmpty());
-                    assertTrue(localWindowDragEvents.get(0).getAction()
-                            == ACTION_DRAG_STARTED);
+                    assertTrue(localWindowDragEvents.get(0).getAction() == ACTION_DRAG_STARTED);
                     assertTrue(globalInterceptWindowDragEvents.get(0).getAction()
                             == ACTION_DRAG_STARTED);
 
                     // Verify that only the global intercept window receives the clip data with the
                     // resolved activity info for the drag
                     assertNull(localWindowDragEvents.get(0).getClipData());
-                    assertTrue(globalInterceptWindowDragEvents.get(0).getClipData()
-                            .willParcelWithActivityInfo());
+                    assertTrue(globalInterceptWindowDragEvents.get(
+                            0).getClipData().willParcelWithActivityInfo());
 
                     mTarget.reportDropWindow(globalInterceptWindow.mInputChannelToken, 0, 0);
                     mTarget.handleMotionEvent(false, 0, 0);
@@ -330,9 +328,8 @@
                     // Verify the start-drag event has the drag flags
                     final DragEvent dragEvent = dragEvents.get(0);
                     assertTrue(dragEvent.getAction() == ACTION_DRAG_STARTED);
-                    assertTrue(dragEvent.getDragFlags() ==
-                            (View.DRAG_FLAG_GLOBAL
-                                    | View.DRAG_FLAG_START_INTENT_SENDER_ON_UNHANDLED_DRAG));
+                    assertTrue(dragEvent.getDragFlags() == (View.DRAG_FLAG_GLOBAL
+                            | View.DRAG_FLAG_START_INTENT_SENDER_ON_UNHANDLED_DRAG));
 
                     try {
                         mTarget.mDeferDragStateClosed = true;
@@ -340,9 +337,8 @@
                         // // Verify the drop event does not have the drag flags
                         mTarget.handleMotionEvent(false, 0, 0);
                         final DragEvent dropEvent = dragEvents.get(dragEvents.size() - 1);
-                        assertTrue(dropEvent.getDragFlags() ==
-                                (View.DRAG_FLAG_GLOBAL
-                                        | View.DRAG_FLAG_START_INTENT_SENDER_ON_UNHANDLED_DRAG));
+                        assertTrue(dropEvent.getDragFlags() == (View.DRAG_FLAG_GLOBAL
+                                | View.DRAG_FLAG_START_INTENT_SENDER_ON_UNHANDLED_DRAG));
 
                         mTarget.reportDropResult(iwindow, true);
                     } finally {
@@ -385,16 +381,15 @@
             data.putExtra(Intent.EXTRA_USER, user);
         }
         final ClipData clipData = new ClipData(
-                new ClipDescription("drag", new String[] {
-                        MIMETYPE_APPLICATION_ACTIVITY}),
+                new ClipDescription("drag", new String[]{MIMETYPE_APPLICATION_ACTIVITY}),
                 new ClipData.Item(data));
         return clipData;
     }
 
     @Test
     public void testValidateAppShortcutArguments() {
-        doReturn(PERMISSION_GRANTED).when(mWm.mContext)
-                .checkCallingOrSelfPermission(eq(START_TASKS_FROM_RECENTS));
+        doReturn(PERMISSION_GRANTED).when(mWm.mContext).checkCallingOrSelfPermission(
+                eq(START_TASKS_FROM_RECENTS));
         final Session session = createTestSession(mAtm);
         try {
             session.validateAndResolveDragMimeTypeExtras(
@@ -414,8 +409,8 @@
         }
         try {
             session.validateAndResolveDragMimeTypeExtras(
-                    createClipDataForShortcut("test_package", "test_shortcut_id", null),
-                    TEST_UID, TEST_PID, TEST_PACKAGE);
+                    createClipDataForShortcut("test_package", "test_shortcut_id", null), TEST_UID,
+                    TEST_PID, TEST_PACKAGE);
             fail("Expected failure without package name");
         } catch (IllegalArgumentException e) {
             // Expected failure
@@ -424,8 +419,8 @@
 
     @Test
     public void testValidateProfileAppShortcutArguments_notCallingUid() {
-        doReturn(PERMISSION_GRANTED).when(mWm.mContext)
-                .checkCallingOrSelfPermission(eq(START_TASKS_FROM_RECENTS));
+        doReturn(PERMISSION_GRANTED).when(mWm.mContext).checkCallingOrSelfPermission(
+                eq(START_TASKS_FROM_RECENTS));
         final Session session = createTestSession(mAtm);
         final ShortcutServiceInternal shortcutService = mock(ShortcutServiceInternal.class);
         final Intent[] shortcutIntents = new Intent[1];
@@ -438,10 +433,9 @@
         ArgumentCaptor<Integer> callingUser = ArgumentCaptor.forClass(Integer.class);
         session.validateAndResolveDragMimeTypeExtras(
                 createClipDataForShortcut("test_package", "test_shortcut_id",
-                        mock(UserHandle.class)),
-                TEST_PROFILE_UID, TEST_PID, TEST_PACKAGE);
-        verify(shortcutService).createShortcutIntents(callingUser.capture(), any(),
-                any(), any(), anyInt(), anyInt(), anyInt());
+                        mock(UserHandle.class)), TEST_PROFILE_UID, TEST_PID, TEST_PACKAGE);
+        verify(shortcutService).createShortcutIntents(callingUser.capture(), any(), any(), any(),
+                anyInt(), anyInt(), anyInt());
         assertTrue(callingUser.getValue() == UserHandle.getUserId(TEST_PROFILE_UID));
     }
 
@@ -458,20 +452,19 @@
             data.putExtra(Intent.EXTRA_USER, user);
         }
         final ClipData clipData = new ClipData(
-                new ClipDescription("drag", new String[] {
-                        MIMETYPE_APPLICATION_SHORTCUT}),
+                new ClipDescription("drag", new String[]{MIMETYPE_APPLICATION_SHORTCUT}),
                 new ClipData.Item(data));
         return clipData;
     }
 
     @Test
     public void testValidateAppTaskArguments() {
-        doReturn(PERMISSION_GRANTED).when(mWm.mContext)
-                .checkCallingOrSelfPermission(eq(START_TASKS_FROM_RECENTS));
+        doReturn(PERMISSION_GRANTED).when(mWm.mContext).checkCallingOrSelfPermission(
+                eq(START_TASKS_FROM_RECENTS));
         final Session session = createTestSession(mAtm);
         try {
             final ClipData clipData = new ClipData(
-                    new ClipDescription("drag", new String[] { MIMETYPE_APPLICATION_TASK }),
+                    new ClipDescription("drag", new String[]{MIMETYPE_APPLICATION_TASK}),
                     new ClipData.Item(new Intent()));
 
             session.validateAndResolveDragMimeTypeExtras(clipData, TEST_UID, TEST_PID,
@@ -496,8 +489,8 @@
 
     @Test
     public void testValidateFlagsWithPermission() {
-        doReturn(PERMISSION_GRANTED).when(mWm.mContext)
-                .checkCallingOrSelfPermission(eq(START_TASKS_FROM_RECENTS));
+        doReturn(PERMISSION_GRANTED).when(mWm.mContext).checkCallingOrSelfPermission(
+                eq(START_TASKS_FROM_RECENTS));
         final Session session = createTestSession(mAtm);
         try {
             session.validateDragFlags(View.DRAG_FLAG_REQUEST_SURFACE_FOR_RETURN_ANIMATION,
@@ -533,8 +526,8 @@
 
                     // Verify the DRAG_ENDED event does NOT include the drag surface
                     final DragEvent dropEvent = dragEvents.get(dragEvents.size() - 1);
-                    assertTrue(dragEvents.get(dragEvents.size() - 1).getAction()
-                            == ACTION_DRAG_ENDED);
+                    assertTrue(
+                            dragEvents.get(dragEvents.size() - 1).getAction() == ACTION_DRAG_ENDED);
                     assertTrue(dropEvent.getDragSurface() == null);
                 });
     }
@@ -564,8 +557,8 @@
 
                     // Verify the DRAG_ENDED event includes the drag surface
                     final DragEvent dropEvent = dragEvents.get(dragEvents.size() - 1);
-                    assertTrue(dragEvents.get(dragEvents.size() - 1).getAction()
-                            == ACTION_DRAG_ENDED);
+                    assertTrue(
+                            dragEvents.get(dragEvents.size() - 1).getAction() == ACTION_DRAG_ENDED);
                     assertTrue(dropEvent.getDragSurface() != null);
                 });
     }
@@ -591,18 +584,18 @@
         final int invalidXY = 100_000;
         startDrag(View.DRAG_FLAG_GLOBAL | View.DRAG_FLAG_START_INTENT_SENDER_ON_UNHANDLED_DRAG,
                 ClipData.newPlainText("label", "Test"), () -> {
-            // Trigger an unhandled drop and verify the global drag listener was called
-            mTarget.reportDropWindow(mWindow.mInputChannelToken, invalidXY, invalidXY);
-            mTarget.handleMotionEvent(false /* keepHandling */, invalidXY, invalidXY);
-            mTarget.reportDropResult(mWindow.mClient, false);
-            mTarget.onUnhandledDropCallback(true);
-            mToken = null;
-            try {
-                verify(listener, times(1)).onUnhandledDrop(any(), any());
-            } catch (RemoteException e) {
-                fail("Failed to verify unhandled drop: " + e);
-            }
-        });
+                    // Trigger an unhandled drop and verify the global drag listener was called
+                    mTarget.reportDropWindow(mWindow.mInputChannelToken, invalidXY, invalidXY);
+                    mTarget.handleMotionEvent(false /* keepHandling */, invalidXY, invalidXY);
+                    mTarget.reportDropResult(mWindow.mClient, false);
+                    mTarget.onUnhandledDropCallback(true);
+                    mToken = null;
+                    try {
+                        verify(listener, times(1)).onUnhandledDrop(any(), any());
+                    } catch (RemoteException e) {
+                        fail("Failed to verify unhandled drop: " + e);
+                    }
+                });
     }
 
     @Test
@@ -615,17 +608,17 @@
         final int invalidXY = 100_000;
         startDrag(View.DRAG_FLAG_GLOBAL | View.DRAG_FLAG_START_INTENT_SENDER_ON_UNHANDLED_DRAG,
                 ClipData.newPlainText("label", "Test"), () -> {
-            // Trigger an unhandled drop and verify the global drag listener was called
-            mTarget.reportDropWindow(mock(IBinder.class), invalidXY, invalidXY);
-            mTarget.handleMotionEvent(false /* keepHandling */, invalidXY, invalidXY);
-            mTarget.onUnhandledDropCallback(true);
-            mToken = null;
-            try {
-                verify(listener, times(1)).onUnhandledDrop(any(), any());
-            } catch (RemoteException e) {
-                fail("Failed to verify unhandled drop: " + e);
-            }
-        });
+                    // Trigger an unhandled drop and verify the global drag listener was called
+                    mTarget.reportDropWindow(mock(IBinder.class), invalidXY, invalidXY);
+                    mTarget.handleMotionEvent(false /* keepHandling */, invalidXY, invalidXY);
+                    mTarget.onUnhandledDropCallback(true);
+                    mToken = null;
+                    try {
+                        verify(listener, times(1)).onUnhandledDrop(any(), any());
+                    } catch (RemoteException e) {
+                        fail("Failed to verify unhandled drop: " + e);
+                    }
+                });
     }
 
     @Test
@@ -636,18 +629,17 @@
         doReturn(mock(Binder.class)).when(listener).asBinder();
         mTarget.setGlobalDragListener(listener);
         final int invalidXY = 100_000;
-        startDrag(View.DRAG_FLAG_GLOBAL,
-                ClipData.newPlainText("label", "Test"), () -> {
-                    // Trigger an unhandled drop and verify the global drag listener was not called
-                    mTarget.reportDropWindow(mock(IBinder.class), invalidXY, invalidXY);
-                    mTarget.handleMotionEvent(false /* keepHandling */, invalidXY, invalidXY);
-                    mToken = null;
-                    try {
-                        verify(listener, never()).onUnhandledDrop(any(), any());
-                    } catch (RemoteException e) {
-                        fail("Failed to verify unhandled drop: " + e);
-                    }
-                });
+        startDrag(View.DRAG_FLAG_GLOBAL, ClipData.newPlainText("label", "Test"), () -> {
+            // Trigger an unhandled drop and verify the global drag listener was not called
+            mTarget.reportDropWindow(mock(IBinder.class), invalidXY, invalidXY);
+            mTarget.handleMotionEvent(false /* keepHandling */, invalidXY, invalidXY);
+            mToken = null;
+            try {
+                verify(listener, never()).onUnhandledDrop(any(), any());
+            } catch (RemoteException e) {
+                fail("Failed to verify unhandled drop: " + e);
+            }
+        });
     }
 
     @Test
@@ -660,20 +652,22 @@
         final int invalidXY = 100_000;
         startDrag(View.DRAG_FLAG_GLOBAL | View.DRAG_FLAG_START_INTENT_SENDER_ON_UNHANDLED_DRAG,
                 ClipData.newPlainText("label", "Test"), () -> {
-            // Trigger an unhandled drop and verify the global drag listener was called
-            mTarget.reportDropWindow(mock(IBinder.class), invalidXY, invalidXY);
-            mTarget.handleMotionEvent(false /* keepHandling */, invalidXY, invalidXY);
+                    // Trigger an unhandled drop and verify the global drag listener was called
+                    mTarget.reportDropWindow(mock(IBinder.class), invalidXY, invalidXY);
+                    mTarget.handleMotionEvent(false /* keepHandling */, invalidXY, invalidXY);
 
-            // Verify that the unhandled drop listener callback timeout has been scheduled
-            final Handler handler = mTarget.getHandler();
-            assertTrue(handler.hasMessages(MSG_UNHANDLED_DROP_LISTENER_TIMEOUT));
+                    // Verify that the unhandled drop listener callback timeout has been scheduled
+                    final Handler handler = mTarget.getHandler();
+                    assertTrue(handler.hasMessages(MSG_UNHANDLED_DROP_LISTENER_TIMEOUT));
 
-            // Force trigger the timeout and verify that it actually cleans up the drag & timeout
-            handler.handleMessage(Message.obtain(handler, MSG_UNHANDLED_DROP_LISTENER_TIMEOUT));
-            assertFalse(handler.hasMessages(MSG_UNHANDLED_DROP_LISTENER_TIMEOUT));
-            assertFalse(mTarget.dragDropActiveLocked());
-            mToken = null;
-        });
+                    // Force trigger the timeout and verify that it actually cleans up the drag &
+                    // timeout
+                    handler.handleMessage(
+                            Message.obtain(handler, MSG_UNHANDLED_DROP_LISTENER_TIMEOUT));
+                    assertFalse(handler.hasMessages(MSG_UNHANDLED_DROP_LISTENER_TIMEOUT));
+                    assertFalse(mTarget.dragDropActiveLocked());
+                    mToken = null;
+                });
     }
 
     private void doDragAndDrop(int flags, ClipData data, float dropX, float dropY) {
@@ -690,15 +684,13 @@
     private void startDrag(int flag, ClipData data, Runnable r) {
         final SurfaceSession appSession = new SurfaceSession();
         try {
-            final SurfaceControl surface = new SurfaceControl.Builder(appSession)
-                    .setName("drag surface")
-                    .setBufferSize(100, 100)
-                    .setFormat(PixelFormat.TRANSLUCENT)
-                    .build();
+            final SurfaceControl surface = new SurfaceControl.Builder(appSession).setName(
+                    "drag surface").setBufferSize(100, 100).setFormat(
+                    PixelFormat.TRANSLUCENT).build();
 
             assertTrue(mWm.mInputManager.startDragAndDrop(new Binder(), new Binder()));
-            mToken = mTarget.performDrag(TEST_PID, 0, mWindow.mClient,
-                    flag, surface, 0, 0, 0, 0, 0, 0, 0, data);
+            mToken = mTarget.performDrag(TEST_PID, 0, mWindow.mClient, flag, surface, 0, 0, 0, 0, 0,
+                    0, 0, data);
             assertNotNull(mToken);
 
             r.run();
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotCacheTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotCacheTest.java
index 29f48b8..f145b40 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotCacheTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotCacheTest.java
@@ -65,33 +65,27 @@
     public void testAppRemoved() {
         final WindowState window = createWindow(null, FIRST_APPLICATION_WINDOW, "window");
         mCache.putSnapshot(window.getTask(), createSnapshot());
-        assertNotNull(mCache.getSnapshot(window.getTask().mTaskId, 0 /* userId */,
-                false /* restoreFromDisk */, false /* isLowResolution */));
+        assertNotNull(mCache.getSnapshot(window.getTask().mTaskId, false /* isLowResolution */));
         mCache.onAppRemoved(window.mActivityRecord);
-        assertNull(mCache.getSnapshot(window.getTask().mTaskId, 0 /* userId */,
-                false /* restoreFromDisk */, false /* isLowResolution */));
+        assertNull(mCache.getSnapshot(window.getTask().mTaskId, false /* isLowResolution */));
     }
 
     @Test
     public void testAppDied() {
         final WindowState window = createWindow(null, FIRST_APPLICATION_WINDOW, "window");
         mCache.putSnapshot(window.getTask(), createSnapshot());
-        assertNotNull(mCache.getSnapshot(window.getTask().mTaskId, 0 /* userId */,
-                false /* restoreFromDisk */, false /* isLowResolution */));
+        assertNotNull(mCache.getSnapshot(window.getTask().mTaskId, false /* isLowResolution */));
         mCache.onAppDied(window.mActivityRecord);
-        assertNull(mCache.getSnapshot(window.getTask().mTaskId, 0 /* userId */,
-                false /* restoreFromDisk */, false /* isLowResolution */));
+        assertNull(mCache.getSnapshot(window.getTask().mTaskId, false /* isLowResolution */));
     }
 
     @Test
     public void testTaskRemoved() {
         final WindowState window = createWindow(null, FIRST_APPLICATION_WINDOW, "window");
         mCache.putSnapshot(window.getTask(), createSnapshot());
-        assertNotNull(mCache.getSnapshot(window.getTask().mTaskId, 0 /* userId */,
-                false /* restoreFromDisk */, false /* isLowResolution */));
+        assertNotNull(mCache.getSnapshot(window.getTask().mTaskId, false /* isLowResolution */));
         mCache.onIdRemoved(window.getTask().mTaskId);
-        assertNull(mCache.getSnapshot(window.getTask().mTaskId, 0 /* userId */,
-                false /* restoreFromDisk */, false /* isLowResolution */));
+        assertNull(mCache.getSnapshot(window.getTask().mTaskId, false /* isLowResolution */));
     }
 
     @Test
@@ -99,16 +93,14 @@
         final WindowState window = createWindow(null, FIRST_APPLICATION_WINDOW, "window");
         mPersister.persistSnapshot(window.getTask().mTaskId, mWm.mCurrentUserId, createSnapshot());
         mSnapshotPersistQueue.waitForQueueEmpty();
-        assertNull(mCache.getSnapshot(window.getTask().mTaskId, mWm.mCurrentUserId,
-                false /* restoreFromDisk */, false /* isLowResolution */));
+        assertNull(mCache.getSnapshot(window.getTask().mTaskId, false /* isLowResolution */));
 
         // Load it from disk
-        assertNotNull(mCache.getSnapshot(window.getTask().mTaskId, mWm.mCurrentUserId,
-                true /* restoreFromDisk */, true /* isLowResolution */));
+        assertNotNull(mCache.getSnapshotFromDisk(window.getTask().mTaskId, mWm.mCurrentUserId,
+                true /* isLowResolution */, TaskSnapshot.REFERENCE_NONE));
 
         // Make sure it's not in the cache now.
-        assertNull(mCache.getSnapshot(window.getTask().mTaskId, mWm.mCurrentUserId,
-                false /* restoreFromDisk */, false /* isLowResolution */));
+        assertNull(mCache.getSnapshot(window.getTask().mTaskId, false /* isLowResolution */));
     }
 
     @Test
@@ -116,20 +108,20 @@
         final WindowState window = createWindow(null, FIRST_APPLICATION_WINDOW, "window");
         mPersister.persistSnapshot(window.getTask().mTaskId, mWm.mCurrentUserId, createSnapshot());
         mSnapshotPersistQueue.waitForQueueEmpty();
-        assertNull(mCache.getSnapshot(window.getTask().mTaskId, mWm.mCurrentUserId,
-                false /* restoreFromDisk */, false /* isLowResolution */));
+        assertNull(mCache.getSnapshot(window.getTask().mTaskId, false /* isLowResolution */));
 
         // Load it from disk
-        assertNotNull(mCache.getSnapshot(window.getTask().mTaskId, mWm.mCurrentUserId,
-                true /* restoreFromDisk */, false /* isLowResolution */));
+        assertNotNull(mCache.getSnapshotFromDisk(window.getTask().mTaskId, mWm.mCurrentUserId,
+                false/* isLowResolution */, TaskSnapshot.REFERENCE_NONE));
     }
 
     @Test
     public void testClearCache() {
         final WindowState window = createWindow(null, FIRST_APPLICATION_WINDOW, "window");
         mCache.putSnapshot(window.getTask(), mSnapshot);
-        assertEquals(mSnapshot, mCache.getSnapshot(window.getTask().mTaskId, 0, false, false));
+        assertEquals(mSnapshot, mCache.getSnapshot(window.getTask().mTaskId,
+                false /* isLowResolution */));
         mCache.clearRunningCache();
-        assertNull(mCache.getSnapshot(window.getTask().mTaskId, 0, false, false));
+        assertNull(mCache.getSnapshot(window.getTask().mTaskId, false /* isLowResolution */));
     }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotLowResDisabledTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotLowResDisabledTest.java
index 7432537..9bde066 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotLowResDisabledTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotLowResDisabledTest.java
@@ -129,23 +129,20 @@
         final WindowState window = createWindow(null, FIRST_APPLICATION_WINDOW, "window");
         mPersister.persistSnapshot(window.getTask().mTaskId, mWm.mCurrentUserId, createSnapshot());
         mSnapshotPersistQueue.waitForQueueEmpty();
-        assertNull(mCache.getSnapshot(window.getTask().mTaskId, mWm.mCurrentUserId,
-                false /* restoreFromDisk */, false /* isLowResolution */));
+        assertNull(mCache.getSnapshot(window.getTask().mTaskId, false /* isLowResolution */));
 
         // Attempt to load the low-res snapshot from the disk
-        assertNull(mCache.getSnapshot(window.getTask().mTaskId, mWm.mCurrentUserId,
-                true /* restoreFromDisk */, true /* isLowResolution */));
+        assertNull(mCache.getSnapshotFromDisk(window.getTask().mTaskId, mWm.mCurrentUserId,
+                true/* isLowResolution */, TaskSnapshot.REFERENCE_NONE));
 
         // Load the high-res (default) snapshot from disk
-        assertNotNull(mCache.getSnapshot(window.getTask().mTaskId, mWm.mCurrentUserId,
-                true /* restoreFromDisk */, false /* isLowResolution */));
+        assertNotNull(mCache.getSnapshotFromDisk(window.getTask().mTaskId, mWm.mCurrentUserId,
+                false /* isLowResolution */, TaskSnapshot.REFERENCE_NONE));
 
         // Make sure it's not in the cache now.
-        assertNull(mCache.getSnapshot(window.getTask().mTaskId, mWm.mCurrentUserId,
-                false /* restoreFromDisk */, true /* isLowResolution */));
+        assertNull(mCache.getSnapshot(window.getTask().mTaskId, true /* isLowResolution */));
 
         // Make sure it's not in the cache now.
-        assertNull(mCache.getSnapshot(window.getTask().mTaskId, mWm.mCurrentUserId,
-                false /* restoreFromDisk */, false /* isLowResolution */));
+        assertNull(mCache.getSnapshot(window.getTask().mTaskId, false /* isLowResolution */));
     }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
index 9602ae2..eb89a9f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
@@ -31,6 +31,7 @@
 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.spyOn;
+import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
 import static com.android.window.flags.Flags.multiCrop;
 
 import static com.google.common.truth.Truth.assertThat;
@@ -412,6 +413,49 @@
         assertFalse(token.isVisible());
     }
 
+    @Test
+    public void testWallpaperTokenVisibilityWithTarget() {
+        mSetFlagsRule.enableFlags(
+                com.android.window.flags.Flags.FLAG_ENSURE_WALLPAPER_IN_TRANSITIONS);
+        final DisplayContent dc = mDisplayContent;
+        final WindowState wallpaperWindow = createWallpaperWindow(dc);
+        final WallpaperWindowToken wallpaperToken = wallpaperWindow.mToken.asWallpaperToken();
+        final WindowState wallpaperTarget = createWallpaperTargetWindow(dc);
+        dc.mWallpaperController.adjustWallpaperWindows();
+        assertEquals(wallpaperTarget, dc.mWallpaperController.getWallpaperTarget());
+        assertTrue(wallpaperToken.isVisibleRequested());
+        assertTrue(wallpaperToken.isVisible());
+
+        registerTestTransitionPlayer();
+        // Assume that another activity is opening and occludes the wallpaper target activity.
+        Transition transition = dc.mTransitionController.createTransition(TRANSIT_OPEN);
+        transition.start();
+        wallpaperTarget.mActivityRecord.setVisibility(false);
+        assertTrue(wallpaperToken.inTransition());
+        waitUntilHandlersIdle();
+        assertFalse("Invisible requested with target", wallpaperToken.isVisibleRequested());
+        assertTrue(wallpaperToken.isVisible());
+
+        transition.onTransactionReady(transition.getSyncId(), mTransaction);
+        dc.mTransitionController.finishTransition(ActionChain.testFinish(transition));
+        assertFalse(wallpaperToken.isVisibleRequested());
+        assertFalse("Commit wallpaper to invisible", wallpaperToken.isVisible());
+        assertTrue((dc.pendingLayoutChanges & FINISH_LAYOUT_REDO_WALLPAPER) != 0);
+        dc.pendingLayoutChanges = 0;
+        dc.mWallpaperController.adjustWallpaperWindows();
+        assertNull(dc.mWallpaperController.getWallpaperTarget());
+
+        // Assume that top activity is closing and the wallpaper target activity becomes visible.
+        transition = dc.mTransitionController.createTransition(TRANSIT_CLOSE);
+        transition.start();
+        wallpaperTarget.mActivityRecord.setVisibility(true);
+        assertTrue((dc.pendingLayoutChanges & FINISH_LAYOUT_REDO_WALLPAPER) != 0);
+        dc.mWallpaperController.adjustWallpaperWindows();
+        assertTrue(wallpaperToken.inTransition());
+        assertTrue("Visible requested with target", wallpaperToken.isVisibleRequested());
+        assertEquals(wallpaperTarget, dc.mWallpaperController.getWallpaperTarget());
+    }
+
     private static void prepareSmallerSecondDisplay(DisplayContent dc, int width, int height) {
         spyOn(dc.mWmService);
         DisplayInfo firstDisplay = dc.getDisplayInfo();
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
index 69df66e..94c7a32 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
@@ -253,7 +253,7 @@
         final Session session = createTestSession(mAtm, wpc.getPid(), wpc.mUid);
         spyOn(session);
         assertTrue(session.mCanAddInternalSystemWindow);
-        final WindowState window = createWindow(null, LayoutParams.TYPE_PHONE, "win");
+        final WindowState window = newWindowBuilder("win", LayoutParams.TYPE_PHONE).build();
         session.onWindowSurfaceVisibilityChanged(window, true /* visible */);
         verify(session).setHasOverlayUi(true);
         session.onWindowSurfaceVisibilityChanged(window, false /* visible */);
@@ -262,7 +262,7 @@
 
     @Test
     public void testRelayoutExitingWindow() {
-        final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, "appWin");
+        final WindowState win = newWindowBuilder("appWin", TYPE_BASE_APPLICATION).build();
         win.mWinAnimator.mDrawState = WindowStateAnimator.HAS_DRAWN;
         win.mWinAnimator.mSurfaceControl = mock(SurfaceControl.class);
         spyOn(win.mTransitionController);
@@ -396,7 +396,7 @@
             int startPrivateFlags, int newFlags, int newPrivateFlags, int expectedChangedFlags,
             int expectedChangedPrivateFlags, int expectedFlagsValue,
             int expectedPrivateFlagsValue) {
-        final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, "appWin");
+        final WindowState win = newWindowBuilder("appWin", TYPE_BASE_APPLICATION).build();
         win.mRelayoutCalled = !firstRelayout;
         mWm.mWindowMap.put(win.mClient.asBinder(), win);
         spyOn(mDisplayContent.mDwpcHelper);
@@ -529,7 +529,7 @@
     public void testAddWindowWithSubWindowTypeByWindowContext() {
         spyOn(mWm.mWindowContextListenerController);
 
-        final WindowState parentWin = createWindow(null, TYPE_INPUT_METHOD, "ime");
+        final WindowState parentWin = newWindowBuilder("ime", TYPE_INPUT_METHOD).build();
         final IBinder parentToken = parentWin.mToken.token;
         parentWin.mAttrs.token = parentToken;
         mWm.mWindowMap.put(parentToken, parentWin);
@@ -1260,8 +1260,8 @@
         final IWindow window = mock(IWindow.class);
         final IBinder binder = mock(IBinder.class);
         doReturn(binder).when(window).asBinder();
-        final WindowState windowState =
-                createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "appWin", window);
+        final WindowState windowState = newWindowBuilder("appWin",
+                TYPE_BASE_APPLICATION).setDisplay(mDisplayContent).setClientWindow(window).build();
         doNothing().when(mWm.mContext).enforceCallingOrSelfPermission(anyString(), anyString());
         doReturn(windowState).when(mWm).getFocusedWindowLocked();
         doReturn(windowState).when(mWm.mRoot).getCurrentInputMethodWindow();
@@ -1274,7 +1274,6 @@
 
     @Test
     public void testInputDeviceNotifyConfigurationChanged() {
-        mSetFlagsRule.enableFlags(Flags.FLAG_FILTER_IRRELEVANT_INPUT_DEVICE_CHANGE);
         spyOn(mDisplayContent);
         doReturn(false).when(mDisplayContent).sendNewConfiguration();
         final InputDevice deviceA = mock(InputDevice.class);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index b27025c..ce0d912 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -89,6 +89,7 @@
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.service.voice.IVoiceInteractionSession;
+import android.tools.function.Supplier;
 import android.util.MergedConfiguration;
 import android.util.SparseArray;
 import android.view.Display;
@@ -584,14 +585,6 @@
     }
 
     // TODO: Move these calls to a builder?
-    WindowState createWindow(WindowState parent, int type, DisplayContent dc, String name,
-            IWindow iwindow) {
-        final WindowToken token = createWindowToken(
-                dc, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, type);
-        return createWindow(parent, type, token, name, 0 /* ownerId */,
-                false /* ownerCanAddInternalSystemWindow */, iwindow);
-    }
-
     WindowState createWindow(WindowState parent, int type, String name) {
         return (parent == null)
                 ? createWindow(parent, type, mDisplayContent, name)
@@ -1804,6 +1797,124 @@
         }
     }
 
+    protected WindowStateBuilder newWindowBuilder(String name, int type) {
+        return new WindowStateBuilder(name, type, mWm, mDisplayContent, mIWindow,
+                this::getTestSession, this::createWindowToken);
+    }
+
+    /**
+     * Builder for creating new window.
+     */
+    protected static class WindowStateBuilder {
+        private final String mName;
+        private final int mType;
+        private final WindowManagerService mWm;
+        private final DisplayContent mDefaultTargetDisplay;
+        private final Supplier<WindowToken, Session> mSessionSupplier;
+        private final WindowTokenCreator mWindowTokenCreator;
+
+        private int mActivityType = ACTIVITY_TYPE_STANDARD;
+        private IWindow mClientWindow;
+        private boolean mOwnerCanAddInternalSystemWindow = false;
+        private int mOwnerId = 0;
+        private WindowState mParent;
+        private DisplayContent mTargetDisplay;
+        private int mWindowingMode = WINDOWING_MODE_FULLSCREEN;
+        private WindowToken mWindowToken;
+
+        WindowStateBuilder(String name, int type, WindowManagerService windowManagerService,
+                DisplayContent dc, IWindow iWindow, Supplier<WindowToken, Session> sessionSupplier,
+                WindowTokenCreator windowTokenCreator) {
+            mName = name;
+            mType = type;
+            mClientWindow = iWindow;
+            mDefaultTargetDisplay = dc;
+            mSessionSupplier = sessionSupplier;
+            mWindowTokenCreator = windowTokenCreator;
+            mWm = windowManagerService;
+        }
+
+        WindowStateBuilder setActivityType(int activityType) {
+            mActivityType = activityType;
+            return this;
+        }
+
+        WindowStateBuilder setClientWindow(IWindow clientWindow) {
+            mClientWindow = clientWindow;
+            return this;
+        }
+
+        WindowStateBuilder setDisplay(DisplayContent displayContent) {
+            mTargetDisplay = displayContent;
+            return this;
+        }
+
+        WindowStateBuilder setOwnerCanAddInternalSystemWindow(
+                boolean ownerCanAddInternalSystemWindow) {
+            mOwnerCanAddInternalSystemWindow = ownerCanAddInternalSystemWindow;
+            return this;
+        }
+
+        WindowStateBuilder setOwnerId(int ownerId) {
+            mOwnerId = ownerId;
+            return this;
+        }
+
+        WindowStateBuilder setParent(WindowState parent) {
+            mParent = parent;
+            return this;
+        }
+
+        WindowStateBuilder setWindowToken(WindowToken token) {
+            mWindowToken = token;
+            return this;
+        }
+
+        WindowStateBuilder setWindowingMode(int windowingMode) {
+            mWindowingMode = windowingMode;
+            return this;
+        }
+
+        WindowState build() {
+            SystemServicesTestRule.checkHoldsLock(mWm.mGlobalLock);
+
+            final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(mType);
+            attrs.setTitle(mName);
+            attrs.packageName = "test";
+
+            assertFalse(
+                    "targetDisplay shouldn't be specified together with windowToken, since"
+                            + " windowToken will be derived from targetDisplay.",
+                    mWindowToken != null && mTargetDisplay != null);
+
+            if (mWindowToken == null) {
+                if (mTargetDisplay != null) {
+                    mWindowToken = mWindowTokenCreator.createWindowToken(mTargetDisplay,
+                            mWindowingMode, mActivityType, mType);
+                } else if (mParent != null) {
+                    mWindowToken = mParent.mToken;
+                } else {
+                    // Use default mDisplayContent as window token.
+                    mWindowToken = mWindowTokenCreator.createWindowToken(mDefaultTargetDisplay,
+                            mWindowingMode, mActivityType, mType);
+                }
+            }
+
+            final WindowState w = new WindowState(mWm, mSessionSupplier.get(mWindowToken),
+                    mClientWindow, mWindowToken, mParent, OP_NONE, attrs, VISIBLE, mOwnerId,
+                    UserHandle.getUserId(mOwnerId), mOwnerCanAddInternalSystemWindow);
+            // TODO: Probably better to make this call in the WindowState ctor to avoid errors with
+            // adding it to the token...
+            mWindowToken.addWindow(w);
+            return w;
+        }
+
+        interface WindowTokenCreator {
+            WindowToken createWindowToken(DisplayContent dc, int windowingMode, int activityType,
+                    int type);
+        }
+    }
+
     static class TestStartingWindowOrganizer extends WindowOrganizerTests.StubOrganizer {
         private final ActivityTaskManagerService mAtm;
         private final WindowManagerService mWMService;
diff --git a/services/usb/java/com/android/server/usb/UsbAlsaDevice.java b/services/usb/java/com/android/server/usb/UsbAlsaDevice.java
index c508fa9..ce3cd29 100644
--- a/services/usb/java/com/android/server/usb/UsbAlsaDevice.java
+++ b/services/usb/java/com/android/server/usb/UsbAlsaDevice.java
@@ -26,6 +26,7 @@
 
 import com.android.internal.util.dump.DualDumpOutputStream;
 import com.android.server.audio.AudioService;
+import com.android.server.usb.flags.Flags;
 
 import java.util.Arrays;
 
@@ -211,6 +212,9 @@
         mIsSelected[direction] = true;
         mState[direction] = 0;
         startJackDetect();
+        if (direction == OUTPUT && Flags.maximizeUsbAudioVolumeWhenConnecting()) {
+            nativeSetVolume(mCardNum, 1.0f /*volume*/);
+        }
         updateWiredDeviceConnectionState(direction, true /*enable*/);
     }
 
@@ -412,5 +416,7 @@
 
         return result;
     }
+
+    private native void nativeSetVolume(int card, float volume);
 }
 
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 15c8b13..c65f784 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -70,6 +70,7 @@
 import android.os.Message;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
+import android.os.SELinux;
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.os.UEventObserver;
@@ -161,6 +162,11 @@
     private static final String MIDI_ALSA_PATH =
             "/sys/class/android_usb/android0/f_midi/alsa";
 
+    /**
+     * The minimum SELinux genfs labels version that supports udc sysfs genfs context.
+     */
+    private static final int MIN_SELINUX_GENFS_LABELS_VERSION = 202404;
+
     private static final int MSG_UPDATE_STATE = 0;
     private static final int MSG_ENABLE_ADB = 1;
     private static final int MSG_SET_CURRENT_FUNCTIONS = 2;
@@ -445,7 +451,8 @@
 
         mEnableUdcSysfsUsbStateUpdate =
                 android.hardware.usb.flags.Flags.enableUdcSysfsUsbStateUpdate()
-                && context.getResources().getBoolean(R.bool.config_enableUdcSysfsUsbStateUpdate);
+                && context.getResources().getBoolean(R.bool.config_enableUdcSysfsUsbStateUpdate)
+                && SELinux.getGenfsLabelsVersion() > MIN_SELINUX_GENFS_LABELS_VERSION;
 
         if (mEnableUdcSysfsUsbStateUpdate) {
             mUEventObserver.startObserving(UDC_SUBSYS_MATCH);
diff --git a/services/usb/java/com/android/server/usb/flags/usb_flags.aconfig b/services/usb/java/com/android/server/usb/flags/usb_flags.aconfig
index a2d0efd..dfbd74c 100644
--- a/services/usb/java/com/android/server/usb/flags/usb_flags.aconfig
+++ b/services/usb/java/com/android/server/usb/flags/usb_flags.aconfig
@@ -21,3 +21,10 @@
     description: "This flag checks if phone is unlocked after boot"
     bug: "73654179"
 }
+
+flag {
+    name: "maximize_usb_audio_volume_when_connecting"
+    namespace: "usb"
+    description: "This flag maximizes the usb audio volume when it is connected"
+    bug: "245041322"
+}
diff --git a/telephony/java/android/telephony/Annotation.java b/telephony/java/android/telephony/Annotation.java
index 09b18b6..8fe107c 100644
--- a/telephony/java/android/telephony/Annotation.java
+++ b/telephony/java/android/telephony/Annotation.java
@@ -109,7 +109,6 @@
             //TelephonyManager.NETWORK_TYPE_LTE_CA,
 
             TelephonyManager.NETWORK_TYPE_NR,
-            TelephonyManager.NETWORK_TYPE_NB_IOT_NTN,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface NetworkType {
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 478ec5c..fa4ec16 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -9988,6 +9988,19 @@
             "satellite_data_support_mode_int";
 
     /**
+     * Determines whether data roaming off setting should be ignored and satellite data should be
+     * allowed even when data roaming is off.
+     *
+     * If the carrier would like to allow the device to use satellite connection when data roaming
+     * is off, this key should be set to {@code true}.
+     *
+     * The default value is {@code false} i.e. disallow satellite data when data roaming is off.
+     */
+    @FlaggedApi(Flags.FLAG_SATELLITE_25Q4_APIS)
+    public static final String KEY_SATELLITE_IGNORE_DATA_ROAMING_SETTING_BOOL =
+        "satellite_ignore_data_roaming_setting_bool";
+
+    /**
      * Determine whether to override roaming Wi-Fi Calling preference when device is connected to
      * non-terrestrial network.
      * {@code true}  - roaming preference cannot be changed by user independently.
@@ -11388,6 +11401,7 @@
         sDefaults.putBoolean(KEY_REMOVE_SATELLITE_PLMN_IN_MANUAL_NETWORK_SCAN_BOOL, true);
         sDefaults.putInt(KEY_SATELLITE_DATA_SUPPORT_MODE_INT,
                 CarrierConfigManager.SATELLITE_DATA_SUPPORT_ONLY_RESTRICTED);
+        sDefaults.putBoolean(KEY_SATELLITE_IGNORE_DATA_ROAMING_SETTING_BOOL, false);
         sDefaults.putBoolean(KEY_OVERRIDE_WFC_ROAMING_MODE_WHILE_USING_NTN_BOOL, true);
         sDefaults.putInt(KEY_SATELLITE_ENTITLEMENT_STATUS_REFRESH_DAYS_INT, 7);
         sDefaults.putBoolean(KEY_SATELLITE_ENTITLEMENT_SUPPORTED_BOOL, false);
diff --git a/telephony/java/android/telephony/RadioAccessFamily.java b/telephony/java/android/telephony/RadioAccessFamily.java
index 8b52f07..90d6f89 100644
--- a/telephony/java/android/telephony/RadioAccessFamily.java
+++ b/telephony/java/android/telephony/RadioAccessFamily.java
@@ -66,9 +66,6 @@
     // 5G
     public static final int RAF_NR = (int) TelephonyManager.NETWORK_TYPE_BITMASK_NR;
 
-    /** NB-IOT (Narrowband Internet of Things) over Non-Terrestrial-Networks technology. */
-    public static final int RAF_NB_IOT_NTN = (int) TelephonyManager.NETWORK_TYPE_BITMASK_NB_IOT_NTN;
-
     // Grouping of RAFs
     // 2G
     private static final int GSM = RAF_GSM | RAF_GPRS | RAF_EDGE;
@@ -83,9 +80,6 @@
     // 5G
     private static final int NR = RAF_NR;
 
-    /** Non-Terrestrial Network. */
-    private static final int NB_IOT_NTN = RAF_NB_IOT_NTN;
-
     /* Phone ID of phone */
     private int mPhoneId;
 
@@ -264,7 +258,7 @@
         raf = ((EVDO & raf) > 0) ? (EVDO | raf) : raf;
         raf = ((LTE & raf) > 0) ? (LTE | raf) : raf;
         raf = ((NR & raf) > 0) ? (NR | raf) : raf;
-        raf = ((NB_IOT_NTN & raf) > 0) ? (NB_IOT_NTN | raf) : raf;
+
         return raf;
     }
 
@@ -370,7 +364,6 @@
             case "WCDMA":   return WCDMA;
             case "LTE_CA":  return RAF_LTE_CA;
             case "NR":      return RAF_NR;
-            case "NB_IOT_NTN": return RAF_NB_IOT_NTN;
             default:        return RAF_UNKNOWN;
         }
     }
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index f8c3287..127bbff 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -233,12 +233,6 @@
     public static final int  RIL_RADIO_TECHNOLOGY_NR = 20;
 
     /**
-     * 3GPP NB-IOT (Narrowband Internet of Things) over Non-Terrestrial-Networks technology.
-     * @hide
-     */
-    public static final int RIL_RADIO_TECHNOLOGY_NB_IOT_NTN = 21;
-
-    /**
      * RIL Radio Annotation
      * @hide
      */
@@ -264,16 +258,14 @@
         ServiceState.RIL_RADIO_TECHNOLOGY_TD_SCDMA,
         ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN,
         ServiceState.RIL_RADIO_TECHNOLOGY_LTE_CA,
-        ServiceState.RIL_RADIO_TECHNOLOGY_NR,
-        ServiceState.RIL_RADIO_TECHNOLOGY_NB_IOT_NTN
-    })
+        ServiceState.RIL_RADIO_TECHNOLOGY_NR})
     public @interface RilRadioTechnology {}
 
 
     /**
      * The number of the radio technologies.
      */
-    private static final int NEXT_RIL_RADIO_TECHNOLOGY = 22;
+    private static final int NEXT_RIL_RADIO_TECHNOLOGY = 21;
 
     /** @hide */
     public static final int RIL_RADIO_CDMA_TECHNOLOGY_BITMASK =
@@ -1133,9 +1125,6 @@
             case RIL_RADIO_TECHNOLOGY_NR:
                 rtString = "NR_SA";
                 break;
-            case RIL_RADIO_TECHNOLOGY_NB_IOT_NTN:
-                rtString = "NB_IOT_NTN";
-                break;
             default:
                 rtString = "Unexpected";
                 Rlog.w(LOG_TAG, "Unexpected radioTechnology=" + rt);
@@ -1679,8 +1668,6 @@
                 return TelephonyManager.NETWORK_TYPE_LTE_CA;
             case RIL_RADIO_TECHNOLOGY_NR:
                 return TelephonyManager.NETWORK_TYPE_NR;
-            case RIL_RADIO_TECHNOLOGY_NB_IOT_NTN:
-                return TelephonyManager.NETWORK_TYPE_NB_IOT_NTN;
             default:
                 return TelephonyManager.NETWORK_TYPE_UNKNOWN;
         }
@@ -1710,7 +1697,6 @@
                 return AccessNetworkType.CDMA2000;
             case RIL_RADIO_TECHNOLOGY_LTE:
             case RIL_RADIO_TECHNOLOGY_LTE_CA:
-            case RIL_RADIO_TECHNOLOGY_NB_IOT_NTN:
                 return AccessNetworkType.EUTRAN;
             case RIL_RADIO_TECHNOLOGY_NR:
                 return AccessNetworkType.NGRAN;
@@ -1771,8 +1757,6 @@
                 return RIL_RADIO_TECHNOLOGY_LTE_CA;
             case TelephonyManager.NETWORK_TYPE_NR:
                 return RIL_RADIO_TECHNOLOGY_NR;
-            case TelephonyManager.NETWORK_TYPE_NB_IOT_NTN:
-                return RIL_RADIO_TECHNOLOGY_NB_IOT_NTN;
             default:
                 return RIL_RADIO_TECHNOLOGY_UNKNOWN;
         }
@@ -1882,8 +1866,7 @@
                 || radioTechnology == RIL_RADIO_TECHNOLOGY_TD_SCDMA
                 || radioTechnology == RIL_RADIO_TECHNOLOGY_IWLAN
                 || radioTechnology == RIL_RADIO_TECHNOLOGY_LTE_CA
-                || radioTechnology == RIL_RADIO_TECHNOLOGY_NR
-                || radioTechnology == RIL_RADIO_TECHNOLOGY_NB_IOT_NTN;
+                || radioTechnology == RIL_RADIO_TECHNOLOGY_NR;
 
     }
 
@@ -1903,8 +1886,7 @@
     public static boolean isPsOnlyTech(int radioTechnology) {
         return radioTechnology == RIL_RADIO_TECHNOLOGY_LTE
                 || radioTechnology == RIL_RADIO_TECHNOLOGY_LTE_CA
-                || radioTechnology == RIL_RADIO_TECHNOLOGY_NR
-                || radioTechnology == RIL_RADIO_TECHNOLOGY_NB_IOT_NTN;
+                || radioTechnology == RIL_RADIO_TECHNOLOGY_NR;
     }
 
     /** @hide */
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 9694b90..24fb8c5 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -3197,12 +3197,6 @@
      * For 5G NSA, the network type will be {@link #NETWORK_TYPE_LTE}.
      */
     public static final int NETWORK_TYPE_NR = TelephonyProtoEnums.NETWORK_TYPE_NR; // 20.
-    /**
-     * 3GPP NB-IOT (Narrowband Internet of Things) over Non-Terrestrial-Networks technology.
-     */
-    @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
-    public static final int NETWORK_TYPE_NB_IOT_NTN =
-            TelephonyProtoEnums.NETWORK_TYPE_NB_IOT_NTN; // 21
 
     private static final @NetworkType int[] NETWORK_TYPES = {
             NETWORK_TYPE_GPRS,
@@ -3279,7 +3273,6 @@
      * @see #NETWORK_TYPE_EHRPD
      * @see #NETWORK_TYPE_HSPAP
      * @see #NETWORK_TYPE_NR
-     * @see #NETWORK_TYPE_NB_IOT_NTN
      *
      * @hide
      */
@@ -3340,7 +3333,6 @@
      * @see #NETWORK_TYPE_EHRPD
      * @see #NETWORK_TYPE_HSPAP
      * @see #NETWORK_TYPE_NR
-     * @see #NETWORK_TYPE_NB_IOT_NTN
      *
      * @throws UnsupportedOperationException If the device does not have
      *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
@@ -3491,8 +3483,6 @@
                 return "LTE_CA";
             case NETWORK_TYPE_NR:
                 return "NR";
-            case NETWORK_TYPE_NB_IOT_NTN:
-                return "NB_IOT_NTN";
             case NETWORK_TYPE_UNKNOWN:
                 return "UNKNOWN";
             default:
@@ -3543,8 +3533,6 @@
                 return NETWORK_TYPE_BITMASK_LTE;
             case NETWORK_TYPE_NR:
                 return NETWORK_TYPE_BITMASK_NR;
-            case NETWORK_TYPE_NB_IOT_NTN:
-                return NETWORK_TYPE_BITMASK_NB_IOT_NTN;
             case NETWORK_TYPE_IWLAN:
                 return NETWORK_TYPE_BITMASK_IWLAN;
             case NETWORK_TYPE_IDEN:
@@ -10339,9 +10327,6 @@
      * This API will result in allowing an intersection of allowed network types for all reasons,
      * including the configuration done through other reasons.
      *
-     * If device supports satellite service, then
-     * {@link #NETWORK_TYPE_NB_IOT_NTN} is added to allowed network types for reason by default.
-     *
      * @param reason the reason the allowed network type change is taking place
      * @param allowedNetworkTypes The bitmask of allowed network type
      * @throws IllegalStateException if the Telephony process is not currently available.
@@ -10391,10 +10376,6 @@
      * <p>Requires permission: android.Manifest.READ_PRIVILEGED_PHONE_STATE or
      * that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
-     * If device supports satellite service, then
-     * {@link #NETWORK_TYPE_NB_IOT_NTN} is added to allowed network types for reason by
-     * default.
-     *
      * @param reason the reason the allowed network type change is taking place
      * @return the allowed network type bitmask
      * @throws IllegalStateException    if the Telephony process is not currently available.
@@ -10461,7 +10442,7 @@
      */
     public static String convertNetworkTypeBitmaskToString(
             @NetworkTypeBitMask long networkTypeBitmask) {
-        String networkTypeName = IntStream.rangeClosed(NETWORK_TYPE_GPRS, NETWORK_TYPE_NB_IOT_NTN)
+        String networkTypeName = IntStream.rangeClosed(NETWORK_TYPE_GPRS, NETWORK_TYPE_NR)
                 .filter(x -> {
                     return (networkTypeBitmask & getBitMaskForNetworkType(x))
                             == getBitMaskForNetworkType(x);
@@ -15143,8 +15124,7 @@
                     NETWORK_TYPE_BITMASK_LTE_CA,
                     NETWORK_TYPE_BITMASK_NR,
                     NETWORK_TYPE_BITMASK_IWLAN,
-                    NETWORK_TYPE_BITMASK_IDEN,
-                    NETWORK_TYPE_BITMASK_NB_IOT_NTN
+                    NETWORK_TYPE_BITMASK_IDEN
             })
     public @interface NetworkTypeBitMask {}
 
@@ -15251,12 +15231,6 @@
      */
     public static final long NETWORK_TYPE_BITMASK_IWLAN = (1 << (NETWORK_TYPE_IWLAN -1));
 
-    /**
-     * network type bitmask indicating the support of readio tech NB IOT NTN.
-     */
-    @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
-    public static final long NETWORK_TYPE_BITMASK_NB_IOT_NTN = (1 << (NETWORK_TYPE_NB_IOT_NTN - 1));
-
     /** @hide */
     public static final long NETWORK_CLASS_BITMASK_2G = NETWORK_TYPE_BITMASK_GSM
                 | NETWORK_TYPE_BITMASK_GPRS
@@ -15285,9 +15259,6 @@
     public static final long NETWORK_CLASS_BITMASK_5G = NETWORK_TYPE_BITMASK_NR;
 
     /** @hide */
-    public static final long NETWORK_CLASS_BITMASK_NTN = NETWORK_TYPE_BITMASK_NB_IOT_NTN;
-
-    /** @hide */
     public static final long NETWORK_STANDARDS_FAMILY_BITMASK_3GPP = NETWORK_TYPE_BITMASK_GSM
             | NETWORK_TYPE_BITMASK_GPRS
             | NETWORK_TYPE_BITMASK_EDGE
@@ -15299,8 +15270,7 @@
             | NETWORK_TYPE_BITMASK_TD_SCDMA
             | NETWORK_TYPE_BITMASK_LTE
             | NETWORK_TYPE_BITMASK_LTE_CA
-            | NETWORK_TYPE_BITMASK_NR
-            | NETWORK_TYPE_BITMASK_NB_IOT_NTN;
+            | NETWORK_TYPE_BITMASK_NR;
 
     /** @hide
      * @deprecated Legacy CDMA is unsupported.
@@ -18341,7 +18311,7 @@
      */
     public static boolean isNetworkTypeValid(@NetworkType int networkType) {
         return networkType >= TelephonyManager.NETWORK_TYPE_UNKNOWN &&
-                networkType <= TelephonyManager.NETWORK_TYPE_NB_IOT_NTN;
+                networkType <= TelephonyManager.NETWORK_TYPE_NR;
     }
 
     /**
diff --git a/telephony/java/android/telephony/satellite/SatelliteManager.java b/telephony/java/android/telephony/satellite/SatelliteManager.java
index 0f23f33..cf807a2 100644
--- a/telephony/java/android/telephony/satellite/SatelliteManager.java
+++ b/telephony/java/android/telephony/satellite/SatelliteManager.java
@@ -2574,6 +2574,8 @@
      * @param executor The executor on which the callback will be called.
      * @param callback The callback to handle the selected satellite subscription changed event.
      *
+     * @return The {@link SatelliteResult} result of the operation.
+     *
      * @throws SecurityException if the caller doesn't have required permission.
      * @throws IllegalStateException if the Telephony process is not currently available.
      *
@@ -3301,7 +3303,7 @@
      * @param executor The executor on which the callback will be called.
      * @param callback The callback to handle the satellite supoprted state changed event.
      *
-     * @return The result of the operation.
+     * @return The {@link SatelliteResult} result of the operation.
      *
      * @throws SecurityException if the caller doesn't have required permission.
      * @throws IllegalStateException if the Telephony process is not currently available.
@@ -3383,7 +3385,7 @@
      *
      * @param executor The executor on which the callback will be called.
      * @param callback The callback to handle satellite communication allowed state changed event.
-     * @return The result of the operation.
+     * @return The {@link SatelliteResult} result of the operation.
      * @throws SecurityException     if the caller doesn't have required permission.
      * @throws IllegalStateException if the Telephony process is not currently available.
      * @hide
@@ -3621,8 +3623,6 @@
      * @throws IllegalStateException if the Telephony process is not currently available.
      * @hide
      */
-    @SystemApi
-    @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
     @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
     public void requestSatelliteDisplayName(
             @NonNull @CallbackExecutor Executor executor,
diff --git a/telephony/java/android/telephony/satellite/SatelliteSessionStats.java b/telephony/java/android/telephony/satellite/SatelliteSessionStats.java
index 0cdba83..556ec1a 100644
--- a/telephony/java/android/telephony/satellite/SatelliteSessionStats.java
+++ b/telephony/java/android/telephony/satellite/SatelliteSessionStats.java
@@ -223,6 +223,10 @@
         return mCountOfUserMessagesInQueueToBeSent;
     }
 
+    public void incrementUserMessagesInQueueToBeSent() {
+        mCountOfUserMessagesInQueueToBeSent++;
+    }
+
     public long getLatencyOfAllSuccessfulUserMessages() {
         return mLatencyOfSuccessfulUserMessages;
     }
@@ -288,6 +292,18 @@
         }
     }
 
+    public void updateCountOfUserMessagesInQueueToBeSent(
+            @SatelliteManager.DatagramType int datagramType) {
+        try {
+            datagramStats.putIfAbsent(datagramType, new SatelliteSessionStats.Builder().build());
+            SatelliteSessionStats data = datagramStats.get(datagramType);
+            data.incrementUserMessagesInQueueToBeSent();
+        } catch (Exception e) {
+            Log.e("SatelliteSessionStats",
+                    "Error while addCountOfUserMessagesInQueueToBeSent: " + e.getMessage());
+        }
+    }
+
     public int getCountOfUnsuccessfulUserMessages(@SatelliteManager.DatagramType int datagramType) {
         SatelliteSessionStats data = datagramStats.get(datagramType);
         return data.getCountOfUnsuccessfulUserMessages();
diff --git a/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/SurfaceControlPictureProfileTest.java b/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/SurfaceControlPictureProfileTest.java
index 135f710..9ac08ed 100644
--- a/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/SurfaceControlPictureProfileTest.java
+++ b/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/SurfaceControlPictureProfileTest.java
@@ -203,12 +203,10 @@
             transaction
                     .setBuffer(mSurfaceControls[i], buffer)
                     .setPictureProfileHandle(mSurfaceControls[i], handle)
-                    .setContentPriority(mSurfaceControls[i], 0);
+                    .setContentPriority(mSurfaceControls[i], 1);
         }
-        // Make the first layer low priority (high value)
-        transaction.setContentPriority(mSurfaceControls[0], 2);
-        // Make the last layer higher priority (lower value)
-        transaction.setContentPriority(mSurfaceControls[maxPictureProfiles], 1);
+        transaction.setContentPriority(mSurfaceControls[0], -1);
+        transaction.setContentPriority(mSurfaceControls[maxPictureProfiles], 0);
         transaction.apply();
 
         pictures = pollMs(picturesQueue, 200);
@@ -219,8 +217,8 @@
         assertThat(stream(pictures).map(picture -> picture.getPictureProfileHandle().getId()))
                 .containsExactlyElementsIn(toIterableRange(2, maxPictureProfiles + 1));
 
-        // Change priority and ensure that the first layer gets access
-        new SurfaceControl.Transaction().setContentPriority(mSurfaceControls[0], 0).apply();
+        // Elevate priority for the first layer and verify it gets to use a profile
+        new SurfaceControl.Transaction().setContentPriority(mSurfaceControls[0], 2).apply();
         pictures = pollMs(picturesQueue, 200);
         assertThat(pictures).isNotNull();
         // Expect all but the last layer to be listed as an active picture
diff --git a/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt b/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt
index 43844f6..aea75d8 100644
--- a/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt
+++ b/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt
@@ -28,10 +28,11 @@
 import android.hardware.display.VirtualDisplay
 import android.hardware.input.InputManager
 import android.hardware.input.InputManagerGlobal
+import android.hardware.input.InputSettings
+import android.hardware.input.KeyGestureEvent
 import android.os.InputEventInjectionSync
 import android.os.SystemClock
 import android.os.test.TestLooper
-import android.platform.test.annotations.EnableFlags
 import android.platform.test.annotations.Presubmit
 import android.platform.test.flag.junit.SetFlagsRule
 import android.provider.Settings
@@ -99,6 +100,7 @@
             .mockStatic(LocalServices::class.java)
             .mockStatic(PermissionChecker::class.java)
             .mockStatic(KeyCharacterMap::class.java)
+            .mockStatic(InputSettings::class.java)
             .build()!!
 
     @get:Rule
@@ -481,32 +483,102 @@
     }
 
     @Test
-    @EnableFlags(com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER)
     fun handleKeyGestures_keyboardBacklight() {
-        service.systemRunning()
-
-        val backlightDownEvent = createKeyEvent(KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_DOWN)
-        service.interceptKeyBeforeDispatching(null, backlightDownEvent, /* policyFlags = */0)
+        val backlightDownEvent =
+            KeyGestureEvent.Builder()
+                .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_DOWN)
+                .setAction(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+                .build()
+        service.handleKeyGestureEvent(backlightDownEvent)
         verify(kbdController).decrementKeyboardBacklight(anyInt())
 
-        val backlightUpEvent = createKeyEvent(KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_UP)
-        service.interceptKeyBeforeDispatching(null, backlightUpEvent, /* policyFlags = */0)
+        val backlightUpEvent =
+            KeyGestureEvent.Builder()
+                .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_UP)
+                .setAction(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+                .build()
+        service.handleKeyGestureEvent(backlightUpEvent)
         verify(kbdController).incrementKeyboardBacklight(anyInt())
     }
 
     @Test
-    @EnableFlags(com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER)
-    fun handleKeyGestures_toggleCapsLock() {
-        service.systemRunning()
+    fun handleKeyGestures_a11yBounceKeysShortcut() {
+        ExtendedMockito.doReturn(true).`when` {
+            InputSettings.isAccessibilityBounceKeysFeatureEnabled()
+        }
+        val toggleBounceKeysEvent =
+            KeyGestureEvent.Builder()
+                .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_BOUNCE_KEYS)
+                .setAction(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+                .build()
+        service.handleKeyGestureEvent(toggleBounceKeysEvent)
+        ExtendedMockito.verify {
+            InputSettings.setAccessibilityBounceKeysThreshold(
+                any(),
+                eq(InputSettings.DEFAULT_BOUNCE_KEYS_THRESHOLD_MILLIS)
+            )
+        }
+    }
 
-        val metaDownEvent = createKeyEvent(KeyEvent.KEYCODE_META_LEFT)
-        service.interceptKeyBeforeDispatching(null, metaDownEvent, /* policyFlags = */0)
-        val altDownEvent =
-            createKeyEvent(KeyEvent.KEYCODE_ALT_LEFT, KeyEvent.META_META_ON, KeyEvent.ACTION_DOWN)
-        service.interceptKeyBeforeDispatching(null, altDownEvent, /* policyFlags = */0)
-        val altUpEvent =
-            createKeyEvent(KeyEvent.KEYCODE_ALT_LEFT, KeyEvent.META_META_ON, KeyEvent.ACTION_UP)
-        service.interceptKeyBeforeDispatching(null, altUpEvent, /* policyFlags = */0)
+    @Test
+    fun handleKeyGestures_a11yMouseKeysShortcut() {
+        ExtendedMockito.doReturn(true).`when` {
+            InputSettings.isAccessibilityMouseKeysFeatureFlagEnabled()
+        }
+        val toggleMouseKeysEvent =
+            KeyGestureEvent.Builder()
+                .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MOUSE_KEYS)
+                .setAction(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+                .build()
+        service.handleKeyGestureEvent(toggleMouseKeysEvent)
+        ExtendedMockito.verify {
+            InputSettings.setAccessibilityMouseKeysEnabled(any(), eq(true))
+        }
+    }
+
+    @Test
+    fun handleKeyGestures_a11yStickyKeysShortcut() {
+        ExtendedMockito.doReturn(true).`when` {
+            InputSettings.isAccessibilityStickyKeysFeatureEnabled()
+        }
+        val toggleStickyKeysEvent =
+            KeyGestureEvent.Builder()
+                .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_STICKY_KEYS)
+                .setAction(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+                .build()
+        service.handleKeyGestureEvent(toggleStickyKeysEvent)
+        ExtendedMockito.verify {
+            InputSettings.setAccessibilityStickyKeysEnabled(any(), eq(true))
+        }
+    }
+
+    @Test
+    fun handleKeyGestures_a11ySlowKeysShortcut() {
+        ExtendedMockito.doReturn(true).`when` {
+            InputSettings.isAccessibilitySlowKeysFeatureFlagEnabled()
+        }
+        val toggleSlowKeysEvent =
+            KeyGestureEvent.Builder()
+                .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_SLOW_KEYS)
+                .setAction(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+                .build()
+        service.handleKeyGestureEvent(toggleSlowKeysEvent)
+        ExtendedMockito.verify {
+            InputSettings.setAccessibilitySlowKeysThreshold(
+                any(),
+                eq(InputSettings.DEFAULT_SLOW_KEYS_THRESHOLD_MILLIS)
+            )
+        }
+    }
+
+    @Test
+    fun handleKeyGestures_toggleCapsLock() {
+        val toggleCapsLockEvent =
+            KeyGestureEvent.Builder()
+                .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK)
+                .setAction(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+                .build()
+        service.handleKeyGestureEvent(toggleCapsLockEvent)
 
         verify(native).toggleCapsLock(anyInt())
     }
@@ -545,25 +617,6 @@
         )
         whenever(windowManagerInternal.getKeyInterceptionInfoFromToken(any())).thenReturn(info)
     }
-
-    private fun createKeyEvent(
-        keycode: Int,
-        modifierState: Int = 0,
-        action: Int = KeyEvent.ACTION_DOWN
-    ): KeyEvent {
-        return KeyEvent(
-            /* downTime = */0,
-            /* eventTime = */0,
-            action,
-            keycode,
-            /* repeat = */0,
-            modifierState,
-            KeyCharacterMap.VIRTUAL_KEYBOARD,
-            /* scancode = */0,
-            /* flags = */0,
-            InputDevice.SOURCE_KEYBOARD
-        )
-    }
 }
 
 private fun <T> whenever(methodCall: T): OngoingStubbing<T> = `when`(methodCall)
diff --git a/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt b/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt
index fafb0e0..4959cb3 100644
--- a/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt
+++ b/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt
@@ -782,6 +782,54 @@
                 KeyEvent.META_META_ON or KeyEvent.META_ALT_ON,
                 intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
             ),
+            TestData(
+                "META + ALT + 'Down' -> Magnification Pan Down",
+                intArrayOf(
+                    KeyEvent.KEYCODE_CTRL_LEFT,
+                    KeyEvent.KEYCODE_ALT_LEFT,
+                    KeyEvent.KEYCODE_DPAD_DOWN
+                ),
+                KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFIER_PAN_DOWN,
+                intArrayOf(KeyEvent.KEYCODE_DPAD_DOWN),
+                KeyEvent.META_CTRL_ON or KeyEvent.META_ALT_ON,
+                intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+            ),
+            TestData(
+                "META + ALT + 'Up' -> Magnification Pan Up",
+                intArrayOf(
+                    KeyEvent.KEYCODE_CTRL_LEFT,
+                    KeyEvent.KEYCODE_ALT_LEFT,
+                    KeyEvent.KEYCODE_DPAD_UP
+                ),
+                KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFIER_PAN_UP,
+                intArrayOf(KeyEvent.KEYCODE_DPAD_UP),
+                KeyEvent.META_CTRL_ON or KeyEvent.META_ALT_ON,
+                intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+            ),
+            TestData(
+                "META + ALT + 'Left' -> Magnification Pan Left",
+                intArrayOf(
+                    KeyEvent.KEYCODE_CTRL_LEFT,
+                    KeyEvent.KEYCODE_ALT_LEFT,
+                    KeyEvent.KEYCODE_DPAD_LEFT
+                ),
+                KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFIER_PAN_LEFT,
+                intArrayOf(KeyEvent.KEYCODE_DPAD_LEFT),
+                KeyEvent.META_CTRL_ON or KeyEvent.META_ALT_ON,
+                intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+            ),
+            TestData(
+                "META + ALT + 'Right' -> Magnification Pan Right",
+                intArrayOf(
+                    KeyEvent.KEYCODE_CTRL_LEFT,
+                    KeyEvent.KEYCODE_ALT_LEFT,
+                    KeyEvent.KEYCODE_DPAD_RIGHT
+                ),
+                KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFIER_PAN_RIGHT,
+                intArrayOf(KeyEvent.KEYCODE_DPAD_RIGHT),
+                KeyEvent.META_CTRL_ON or KeyEvent.META_ALT_ON,
+                intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+            ),
         )
     }
 
diff --git a/tests/Input/src/com/android/test/input/KeyCharacterMapTest.kt b/tests/Input/src/com/android/test/input/KeyCharacterMapTest.kt
new file mode 100644
index 0000000..2818379
--- /dev/null
+++ b/tests/Input/src/com/android/test/input/KeyCharacterMapTest.kt
@@ -0,0 +1,55 @@
+/*
+ * 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.test.input
+
+import android.view.KeyCharacterMap
+import android.view.KeyEvent
+
+import org.junit.Assert.assertEquals
+import org.junit.Test
+
+/**
+ * Tests for {@link KeyCharacterMap}.
+ *
+ * <p>Build/Install/Run:
+ * atest KeyCharacterMapTest
+ *
+ */
+class KeyCharacterMapTest {
+    @Test
+    fun testGetFallback() {
+        // Based off of VIRTUAL kcm fallbacks.
+        val keyCharacterMap = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD)
+
+        // One modifier fallback.
+        assertEquals(
+            keyCharacterMap.getFallbackAction(KeyEvent.KEYCODE_SPACE,
+                KeyEvent.META_CTRL_ON).keyCode,
+            KeyEvent.KEYCODE_LANGUAGE_SWITCH)
+
+        // Multiple modifier fallback.
+        assertEquals(
+            keyCharacterMap.getFallbackAction(KeyEvent.KEYCODE_DEL,
+                KeyEvent.META_CTRL_ON or KeyEvent.META_ALT_ON).keyCode,
+            KeyEvent.KEYCODE_BACK)
+
+        // No default button, fallback only.
+        assertEquals(
+            keyCharacterMap.getFallbackAction(KeyEvent.KEYCODE_BUTTON_A, 0).keyCode,
+            KeyEvent.KEYCODE_DPAD_CENTER)
+    }
+}
diff --git a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
index c64dc72..928e232 100644
--- a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
+++ b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
@@ -19,6 +19,7 @@
 import static android.service.watchdog.ExplicitHealthCheckService.PackageConfig;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
+import static com.android.server.PackageWatchdog.MITIGATION_RESULT_SUCCESS;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -1933,12 +1934,12 @@
             return mImpact;
         }
 
-        public boolean onExecuteHealthCheckMitigation(VersionedPackage versionedPackage,
+        public int onExecuteHealthCheckMitigation(VersionedPackage versionedPackage,
                 int failureReason, int mitigationCount) {
             mMitigatedPackages.add(versionedPackage.getPackageName());
             mMitigationCounts.add(mitigationCount);
             mLastFailureReason = failureReason;
-            return true;
+            return MITIGATION_RESULT_SUCCESS;
         }
 
         public String getUniqueIdentifier() {
@@ -1957,11 +1958,10 @@
             return mImpact;
         }
 
-        public boolean onExecuteBootLoopMitigation(int level) {
-            Slog.w("hrm1243", "I'm here " + level);
+        public int onExecuteBootLoopMitigation(int level) {
             mMitigatedBootLoop = true;
             mBootMitigationCounts.add(level);
-            return true;
+            return MITIGATION_RESULT_SUCCESS;
         }
 
         public boolean mitigatedBootLoop() {
diff --git a/tools/protologtool/src/com/android/protolog/tool/ProtoLogCallProcessorImpl.kt b/tools/protologtool/src/com/android/protolog/tool/ProtoLogCallProcessorImpl.kt
index c3595b7..272d8bb 100644
--- a/tools/protologtool/src/com/android/protolog/tool/ProtoLogCallProcessorImpl.kt
+++ b/tools/protologtool/src/com/android/protolog/tool/ProtoLogCallProcessorImpl.kt
@@ -119,7 +119,8 @@
                         }
 
                         logCallVisitor?.processCall(call, messageString, getLevelForMethodName(
-                            call.name.toString(), call, context), groupMap.getValue(groupName))
+                            call.name.toString(), call, context), groupMap.getValue(groupName),
+                            context.lineNumber)
                     } else if (call.name.id == "init") {
                         // No processing
                     } else {
diff --git a/tools/protologtool/src/com/android/protolog/tool/ProtoLogCallVisitor.kt b/tools/protologtool/src/com/android/protolog/tool/ProtoLogCallVisitor.kt
index 8cd927a..216241a 100644
--- a/tools/protologtool/src/com/android/protolog/tool/ProtoLogCallVisitor.kt
+++ b/tools/protologtool/src/com/android/protolog/tool/ProtoLogCallVisitor.kt
@@ -20,5 +20,11 @@
 import com.github.javaparser.ast.expr.MethodCallExpr
 
 interface ProtoLogCallVisitor {
-    fun processCall(call: MethodCallExpr, messageString: String, level: LogLevel, group: LogGroup)
+    fun processCall(
+        call: MethodCallExpr,
+        messageString: String,
+        level: LogLevel,
+        group: LogGroup,
+        lineNumber: Int
+    )
 }
diff --git a/tools/protologtool/src/com/android/protolog/tool/ProtoLogTool.kt b/tools/protologtool/src/com/android/protolog/tool/ProtoLogTool.kt
index 9222ff4..d8a2954 100644
--- a/tools/protologtool/src/com/android/protolog/tool/ProtoLogTool.kt
+++ b/tools/protologtool/src/com/android/protolog/tool/ProtoLogTool.kt
@@ -69,7 +69,8 @@
         val messageString: String,
         val logLevel: LogLevel,
         val logGroup: LogGroup,
-        val position: String
+        val position: String,
+        val lineNumber: Int,
     )
 
     private fun showHelpAndExit() {
@@ -435,9 +436,10 @@
                 call: MethodCallExpr,
                 messageString: String,
                 level: LogLevel,
-                group: LogGroup
+                group: LogGroup,
+                lineNumber: Int,
             ) {
-                val logCall = LogCall(messageString, level, group, packagePath)
+                val logCall = LogCall(messageString, level, group, packagePath, lineNumber)
                 calls.add(logCall)
             }
         }
diff --git a/tools/protologtool/src/com/android/protolog/tool/SourceTransformer.kt b/tools/protologtool/src/com/android/protolog/tool/SourceTransformer.kt
index c478f58..76df067 100644
--- a/tools/protologtool/src/com/android/protolog/tool/SourceTransformer.kt
+++ b/tools/protologtool/src/com/android/protolog/tool/SourceTransformer.kt
@@ -91,7 +91,8 @@
             call: MethodCallExpr,
             messageString: String,
             level: LogLevel,
-            group: LogGroup
+            group: LogGroup,
+            lineNumber: Int,
         ) {
             validateCall(call)
             val processedCallStatement =
diff --git a/tools/protologtool/src/com/android/protolog/tool/ViewerConfigProtoBuilder.kt b/tools/protologtool/src/com/android/protolog/tool/ViewerConfigProtoBuilder.kt
index de85411..5af2d94 100644
--- a/tools/protologtool/src/com/android/protolog/tool/ViewerConfigProtoBuilder.kt
+++ b/tools/protologtool/src/com/android/protolog/tool/ViewerConfigProtoBuilder.kt
@@ -59,7 +59,7 @@
                         .setLevel(
                             ProtoLogLevel.forNumber(log.logLevel.id))
                         .setGroupId(groupId)
-                        .setLocation(log.position)
+                        .setLocation("${log.position}:${log.lineNumber}")
             )
         }
 
diff --git a/tools/protologtool/tests/com/android/protolog/tool/ProtoLogCallProcessorImplTest.kt b/tools/protologtool/tests/com/android/protolog/tool/ProtoLogCallProcessorImplTest.kt
index 5e50f71..004d97b 100644
--- a/tools/protologtool/tests/com/android/protolog/tool/ProtoLogCallProcessorImplTest.kt
+++ b/tools/protologtool/tests/com/android/protolog/tool/ProtoLogCallProcessorImplTest.kt
@@ -42,7 +42,8 @@
             call: MethodCallExpr,
             messageString: String,
             level: LogLevel,
-            group: LogGroup
+            group: LogGroup,
+            lineNumber: Int,
         ) {
             calls.add(LogCall(call, messageString, level, group))
         }
diff --git a/tools/protologtool/tests/com/android/protolog/tool/SourceTransformerTest.kt b/tools/protologtool/tests/com/android/protolog/tool/SourceTransformerTest.kt
index 6cde7a7..674a907 100644
--- a/tools/protologtool/tests/com/android/protolog/tool/SourceTransformerTest.kt
+++ b/tools/protologtool/tests/com/android/protolog/tool/SourceTransformerTest.kt
@@ -158,7 +158,7 @@
             val visitor = invocation.arguments[1] as ProtoLogCallVisitor
 
             visitor.processCall(code.findAll(MethodCallExpr::class.java)[0], "test %d %f",
-                    LogLevel.WARN, LogGroup("TEST_GROUP", true, true, "WM_TEST"))
+                    LogLevel.WARN, LogGroup("TEST_GROUP", true, true, "WM_TEST"), 123)
 
             invocation.arguments[0] as CompilationUnit
         }
@@ -195,11 +195,11 @@
 
             val calls = code.findAll(MethodCallExpr::class.java)
             visitor.processCall(calls[0], "test %d %f",
-                    LogLevel.WARN, LogGroup("TEST_GROUP", true, true, "WM_TEST"))
+                    LogLevel.WARN, LogGroup("TEST_GROUP", true, true, "WM_TEST"), 456)
             visitor.processCall(calls[1], "test %d %f",
-                    LogLevel.WARN, LogGroup("TEST_GROUP", true, true, "WM_TEST"))
+                    LogLevel.WARN, LogGroup("TEST_GROUP", true, true, "WM_TEST"), 789)
             visitor.processCall(calls[2], "test %d %f",
-                    LogLevel.WARN, LogGroup("TEST_GROUP", true, true, "WM_TEST"))
+                    LogLevel.WARN, LogGroup("TEST_GROUP", true, true, "WM_TEST"), 123)
 
             invocation.arguments[0] as CompilationUnit
         }
@@ -236,7 +236,7 @@
 
             visitor.processCall(code.findAll(MethodCallExpr::class.java)[0],
                     "test %d %f abc %s\n test", LogLevel.WARN, LogGroup("TEST_GROUP",
-                    true, true, "WM_TEST"))
+                    true, true, "WM_TEST"), 123)
 
             invocation.arguments[0] as CompilationUnit
         }
@@ -273,7 +273,7 @@
             val visitor = invocation.arguments[1] as ProtoLogCallVisitor
 
             visitor.processCall(code.findAll(MethodCallExpr::class.java)[0], "test",
-                    LogLevel.WARN, LogGroup("TEST_GROUP", true, true, "WM_TEST"))
+                    LogLevel.WARN, LogGroup("TEST_GROUP", true, true, "WM_TEST"), 456)
 
             invocation.arguments[0] as CompilationUnit
         }
@@ -307,7 +307,7 @@
             val visitor = invocation.arguments[1] as ProtoLogCallVisitor
 
             visitor.processCall(code.findAll(MethodCallExpr::class.java)[0], "test %d %f",
-                    LogLevel.WARN, LogGroup("TEST_GROUP", true, false, "WM_TEST"))
+                    LogLevel.WARN, LogGroup("TEST_GROUP", true, false, "WM_TEST"), 789)
 
             invocation.arguments[0] as CompilationUnit
         }
@@ -344,7 +344,7 @@
 
             visitor.processCall(code.findAll(MethodCallExpr::class.java)[0],
                     "test %d %f abc %s\n test", LogLevel.WARN, LogGroup("TEST_GROUP",
-                    true, false, "WM_TEST"))
+                    true, false, "WM_TEST"), 123)
 
             invocation.arguments[0] as CompilationUnit
         }
diff --git a/tools/protologtool/tests/com/android/protolog/tool/ViewerConfigJsonBuilderTest.kt b/tools/protologtool/tests/com/android/protolog/tool/ViewerConfigJsonBuilderTest.kt
index 1a20d4c..bcbc879 100644
--- a/tools/protologtool/tests/com/android/protolog/tool/ViewerConfigJsonBuilderTest.kt
+++ b/tools/protologtool/tests/com/android/protolog/tool/ViewerConfigJsonBuilderTest.kt
@@ -50,9 +50,9 @@
     fun processClass() {
         val logCallRegistry = ProtoLogTool.LogCallRegistry()
         logCallRegistry.addLogCalls(listOf(
-                LogCall(TEST1.messageString, LogLevel.INFO, GROUP1, PATH),
-                LogCall(TEST2.messageString, LogLevel.DEBUG, GROUP2, PATH),
-                LogCall(TEST3.messageString, LogLevel.ERROR, GROUP3, PATH)))
+                LogCall(TEST1.messageString, LogLevel.INFO, GROUP1, PATH, 123),
+                LogCall(TEST2.messageString, LogLevel.DEBUG, GROUP2, PATH, 456),
+                LogCall(TEST3.messageString, LogLevel.ERROR, GROUP3, PATH, 789)))
 
         val parsedConfig = parseConfig(
             configBuilder.build(GROUPS, logCallRegistry.getStatements()).toString(Charsets.UTF_8))
@@ -69,9 +69,9 @@
     fun processClass_nonUnique() {
         val logCallRegistry = ProtoLogTool.LogCallRegistry()
         logCallRegistry.addLogCalls(listOf(
-                LogCall(TEST1.messageString, LogLevel.INFO, GROUP1, PATH),
-                LogCall(TEST1.messageString, LogLevel.INFO, GROUP1, PATH),
-                LogCall(TEST1.messageString, LogLevel.INFO, GROUP1, PATH)))
+                LogCall(TEST1.messageString, LogLevel.INFO, GROUP1, PATH, 123),
+                LogCall(TEST1.messageString, LogLevel.INFO, GROUP1, PATH, 456),
+                LogCall(TEST1.messageString, LogLevel.INFO, GROUP1, PATH, 789)))
 
         val parsedConfig = parseConfig(
             configBuilder.build(GROUPS, logCallRegistry.getStatements()).toString(Charsets.UTF_8))
diff --git a/tools/protologtool/tests/com/android/protolog/tool/ViewerConfigProtoBuilderTest.kt b/tools/protologtool/tests/com/android/protolog/tool/ViewerConfigProtoBuilderTest.kt
index 74a8de7..dfc6640 100644
--- a/tools/protologtool/tests/com/android/protolog/tool/ViewerConfigProtoBuilderTest.kt
+++ b/tools/protologtool/tests/com/android/protolog/tool/ViewerConfigProtoBuilderTest.kt
@@ -55,8 +55,8 @@
 
         val logCallRegistry = ProtoLogTool.LogCallRegistry()
         logCallRegistry.addLogCalls(listOf(
-            LogCall(TEST1.messageString, LogLevel.INFO, GROUP1, PATH),
-            LogCall(TEST2.messageString, LogLevel.INFO, GROUP2, PATH),
+            LogCall(TEST1.messageString, LogLevel.INFO, GROUP1, PATH, 123),
+            LogCall(TEST2.messageString, LogLevel.INFO, GROUP2, PATH, 456),
         ))
 
         val rawProto = configBuilder.build(GROUPS, logCallRegistry.getStatements())
@@ -65,4 +65,22 @@
         Truth.assertThat(viewerConfig.groupsCount).isEqualTo(GROUPS.size)
         Truth.assertThat(viewerConfig.messagesCount).isLessThan(GROUPS.size)
     }
+
+    @Test
+    fun includesLineNumberInLocation() {
+        val configBuilder = ViewerConfigProtoBuilder()
+
+        val logCallRegistry = ProtoLogTool.LogCallRegistry()
+        logCallRegistry.addLogCalls(listOf(
+            LogCall(TEST1.messageString, LogLevel.INFO, GROUP1, PATH, 123),
+            LogCall(TEST2.messageString, LogLevel.INFO, GROUP2, PATH, 456),
+        ))
+
+        val rawProto = configBuilder.build(GROUPS, logCallRegistry.getStatements())
+
+        val viewerConfig = ProtoLogViewerConfig.parseFrom(rawProto)
+
+        Truth.assertThat(viewerConfig.messagesList[0].location).isEqualTo("$PATH:123")
+        Truth.assertThat(viewerConfig.messagesList[1].location).isEqualTo("$PATH:456")
+    }
 }
\ No newline at end of file
diff --git a/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesMetadataProcessor.kt b/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesMetadataProcessor.kt
index 4a6d4b1..c51c6d6 100644
--- a/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesMetadataProcessor.kt
+++ b/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesMetadataProcessor.kt
@@ -18,9 +18,11 @@
 
 import android.annotation.SdkConstant
 import com.squareup.javapoet.ClassName
+import com.squareup.javapoet.CodeBlock
 import com.squareup.javapoet.FieldSpec
 import com.squareup.javapoet.JavaFile
 import com.squareup.javapoet.MethodSpec
+import com.squareup.javapoet.ParameterizedTypeName
 import com.squareup.javapoet.TypeSpec
 import java.io.IOException
 import javax.annotation.processing.AbstractProcessor
@@ -101,8 +103,8 @@
             TypeSpec.classBuilder("SystemFeaturesMetadata")
                 .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
                 .addJavadoc("@hide")
-                .addField(buildFeatureCount(featureVarNames))
-                .addMethod(buildFeatureIndexLookup(featureVarNames))
+                .addFeatureCount(featureVarNames)
+                .addFeatureIndexLookup(featureVarNames)
                 .build()
 
         try {
@@ -120,19 +122,55 @@
         return true
     }
 
-    private fun buildFeatureCount(featureVarNames: Collection<String>): FieldSpec {
-        return FieldSpec.builder(Int::class.java, "SDK_FEATURE_COUNT")
-            .addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL)
-            .addJavadoc(
-                "# of {@link android.annotation.SdkConstant}` features defined in PackageManager."
-            )
-            .addJavadoc("\n\n@hide")
-            .initializer("\$L", featureVarNames.size)
-            .build()
+    private fun TypeSpec.Builder.addFeatureCount(
+        featureVarNames: Collection<String>
+    ): TypeSpec.Builder {
+        return addField(
+            FieldSpec.builder(Int::class.java, "SDK_FEATURE_COUNT")
+                .addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL)
+                .addJavadoc(
+                    "# of {@link android.annotation.SdkConstant}` features in PackageManager."
+                )
+                .addJavadoc("\n\n@hide")
+                .initializer("\$L", featureVarNames.size)
+                .build()
+        )
     }
 
-    private fun buildFeatureIndexLookup(featureVarNames: Collection<String>): MethodSpec {
-        val methodBuilder =
+    private fun TypeSpec.Builder.addFeatureIndexLookup(
+        featureVarNames: Collection<String>
+    ): TypeSpec.Builder {
+        // NOTE: This was initially implemented in terms of a single, long switch() statement.
+        // However, this resulted in:
+        //   1) relatively large compiled code size for the lookup method (~20KB)
+        //   2) worse runtime lookup performance than a simple ArraySet
+        // The ArraySet approach adds just ~1KB to the code/image and is 2x faster at runtime.
+
+        // Provide the initial capacity of the ArraySet for efficiency.
+        addField(
+            FieldSpec.builder(
+                    ParameterizedTypeName.get(ARRAYSET_CLASS, ClassName.get(String::class.java)),
+                    "sFeatures",
+                )
+                .addModifiers(Modifier.PRIVATE, Modifier.STATIC, Modifier.FINAL)
+                .initializer("new ArraySet<>(\$L)", featureVarNames.size)
+                .build()
+        )
+
+        // Use a temp array + Collections.addAll() to minimizes the generated code size.
+        addStaticBlock(
+            CodeBlock.builder()
+                .add("final \$T[] features = {\n", String::class.java)
+                .indent()
+                .apply { featureVarNames.forEach { add("\$T.\$N,\n", PACKAGEMANAGER_CLASS, it) } }
+                .unindent()
+                .addStatement("}")
+                .addStatement("\$T.addAll(sFeatures, features)", COLLECTIONS_CLASS)
+                .build()
+        )
+
+        // Use ArraySet.indexOf to provide the implicit feature index mapping.
+        return addMethod(
             MethodSpec.methodBuilder("maybeGetSdkFeatureIndex")
                 .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
                 .addJavadoc("@return an index in [0, SDK_FEATURE_COUNT) for features defined ")
@@ -140,21 +178,15 @@
                 .addJavadoc("\n\n@hide")
                 .returns(Int::class.java)
                 .addParameter(String::class.java, "featureName")
-        methodBuilder.beginControlFlow("switch (featureName)")
-        featureVarNames.forEachIndexed { index, featureVarName ->
-            methodBuilder
-                .addCode("case \$T.\$N: ", PACKAGEMANAGER_CLASS, featureVarName)
-                .addStatement("return \$L", index)
-        }
-        methodBuilder
-            .addCode("default: ")
-            .addStatement("return -1")
-            .endControlFlow()
-        return methodBuilder.build()
+                .addStatement("return sFeatures.indexOf(featureName)")
+                .build()
+        )
     }
 
     companion object {
         private val SDK_CONSTANT_ANNOTATION_NAME = SdkConstant::class.qualifiedName
         private val PACKAGEMANAGER_CLASS = ClassName.get("android.content.pm", "PackageManager")
+        private val ARRAYSET_CLASS = ClassName.get("android.util", "ArraySet")
+        private val COLLECTIONS_CLASS = ClassName.get("java.util", "Collections")
     }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.kt b/tools/systemfeatures/tests/src/ArraySet.java
similarity index 61%
copy from packages/SystemUI/src/com/android/keyguard/LockIconViewController.kt
copy to tools/systemfeatures/tests/src/ArraySet.java
index c5012b0..0eb8f29 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.kt
+++ b/tools/systemfeatures/tests/src/ArraySet.java
@@ -14,22 +14,21 @@
  * limitations under the License.
  */
 
-package com.android.keyguard
+package android.util;
 
-import android.view.MotionEvent
-import android.view.View
+import java.util.ArrayList;
 
-/** Controls the [LockIconView]. */
-interface LockIconViewController {
-    fun setLockIconView(lockIconView: View)
+/** Stub for testing, we extend ArrayList to get indexOf() for free. */
+public final class ArraySet<K> extends ArrayList<K> {
+    public ArraySet(int capacity) {
+        super(capacity);
+    }
 
-    fun getTop(): Float
-
-    fun getBottom(): Float
-
-    fun dozeTimeTick()
-
-    fun setAlpha(alpha: Float)
-
-    fun willHandleTouchWhileDozing(event: MotionEvent): Boolean
+    @Override
+    public boolean add(K k) {
+        if (!contains(k)) {
+            return super.add(k);
+        }
+        return false;
+    }
 }